hightop 0.1.4 → 0.2.4
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 +5 -5
- data/CHANGELOG.md +31 -9
- data/LICENSE.txt +1 -1
- data/README.md +61 -12
- data/lib/hightop.rb +13 -37
- data/lib/hightop/enumerable.rb +24 -0
- data/lib/hightop/kicks.rb +41 -0
- data/lib/hightop/mongoid.rb +56 -0
- data/lib/hightop/utils.rb +21 -0
- data/lib/hightop/version.rb +1 -1
- metadata +20 -28
- data/.gitignore +0 -22
- data/.travis.yml +0 -12
- data/Gemfile +0 -4
- data/Rakefile +0 -8
- data/hightop.gemspec +0 -27
- data/test/hightop_test.rb +0 -111
- data/test/test_helper.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e986b06b74104ea5fb99fe92abdb061eab4cc225ba9fa4f34216eadcd704e556
|
4
|
+
data.tar.gz: 5ca8954b8ce39c1212e0bc0b651e22450ff26b17795665b561cc7e4bbb134067
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9d93d7b9edcce9dcfb6f3b7912ef3252454731bcc68d45cc9ff391df854a6fe49e5d7ca269780e55e98cf1481a17ef3472427de69694d6098cfdcacf9e61c7d
|
7
|
+
data.tar.gz: cd86dfd59b77d39482d06d0a6e9f59869ca15eaab6eef6e5cc62f754ad9d3addd781e21749a192a181385620b2508b520ee22da1f2c397566ac82159778a65d2
|
data/CHANGELOG.md
CHANGED
@@ -1,37 +1,59 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.2.4 (2020-09-07)
|
2
|
+
|
3
|
+
- Added warning for non-attribute argument
|
4
|
+
- Added deprecation warning for `uniq`
|
5
|
+
|
6
|
+
## 0.2.3 (2020-06-18)
|
7
|
+
|
8
|
+
- Dropped support for Active Record 4.2 and Ruby 2.3
|
9
|
+
- Fixed deprecation warning in Ruby 2.7
|
10
|
+
|
11
|
+
## 0.2.2 (2019-08-12)
|
12
|
+
|
13
|
+
- Added support for Mongoid
|
14
|
+
|
15
|
+
## 0.2.1 (2019-08-04)
|
16
|
+
|
17
|
+
- Added support for arrays and hashes
|
18
|
+
|
19
|
+
## 0.2.0 (2017-03-19)
|
20
|
+
|
21
|
+
- Use keyword arguments
|
22
|
+
|
23
|
+
## 0.1.4 (2016-02-04)
|
2
24
|
|
3
25
|
- Added `distinct` option to replace `uniq`
|
4
26
|
|
5
|
-
## 0.1.3
|
27
|
+
## 0.1.3 (2015-06-18)
|
6
28
|
|
7
29
|
- Fixed `min` option with `uniq`
|
8
30
|
|
9
|
-
## 0.1.2
|
31
|
+
## 0.1.2 (2014-11-05)
|
10
32
|
|
11
33
|
- Added `min` option
|
12
34
|
|
13
|
-
## 0.1.1
|
35
|
+
## 0.1.1 (2014-07-02)
|
14
36
|
|
15
37
|
- Added `uniq` option
|
16
38
|
- Fixed `Model.limit(n).top`
|
17
39
|
|
18
|
-
## 0.1.0
|
40
|
+
## 0.1.0 (2014-06-11)
|
19
41
|
|
20
42
|
- No changes, just bump
|
21
43
|
|
22
|
-
## 0.0.4
|
44
|
+
## 0.0.4 (2014-06-11)
|
23
45
|
|
24
46
|
- Added support for multiple groups
|
25
47
|
- Added `nil` option
|
26
48
|
|
27
|
-
## 0.0.3
|
49
|
+
## 0.0.3 (2014-06-11)
|
28
50
|
|
29
51
|
- Fixed escaping
|
30
52
|
|
31
|
-
## 0.0.2
|
53
|
+
## 0.0.2 (2014-05-29)
|
32
54
|
|
33
55
|
- Added `limit` parameter
|
34
56
|
|
35
|
-
## 0.0.1
|
57
|
+
## 0.0.1 (2014-05-11)
|
36
58
|
|
37
59
|
- First release
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -2,19 +2,28 @@
|
|
2
2
|
|
3
3
|
A nice shortcut for group count queries
|
4
4
|
|
5
|
-
[](https://travis-ci.org/ankane/hightop)
|
6
|
-
|
7
5
|
```ruby
|
8
6
|
Visit.top(:browser)
|
7
|
+
# {
|
8
|
+
# "Chrome" => 63,
|
9
|
+
# "Safari" => 50,
|
10
|
+
# "Firefox" => 34
|
11
|
+
# }
|
9
12
|
```
|
10
13
|
|
11
|
-
|
14
|
+
Works with Active Record, Mongoid, arrays and hashes
|
15
|
+
|
16
|
+
[](https://travis-ci.org/ankane/hightop)
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Add this line to your application’s Gemfile:
|
12
21
|
|
13
22
|
```ruby
|
14
|
-
|
23
|
+
gem 'hightop'
|
15
24
|
```
|
16
25
|
|
17
|
-
|
26
|
+
## Options
|
18
27
|
|
19
28
|
Limit the results
|
20
29
|
|
@@ -37,7 +46,7 @@ Visit.top([:city, :browser])
|
|
37
46
|
And expressions
|
38
47
|
|
39
48
|
```ruby
|
40
|
-
Visit.top("LOWER(referring_domain)")
|
49
|
+
Visit.top(Arel.sql("LOWER(referring_domain)"))
|
41
50
|
```
|
42
51
|
|
43
52
|
And distinct
|
@@ -52,18 +61,49 @@ And min count
|
|
52
61
|
Visit.top(:city, min: 10)
|
53
62
|
```
|
54
63
|
|
55
|
-
##
|
64
|
+
## User Input
|
56
65
|
|
57
|
-
|
66
|
+
If passing user input as the column, be sure to sanitize it first [like you must](https://rails-sqli.org/) with `group`.
|
58
67
|
|
59
68
|
```ruby
|
60
|
-
|
69
|
+
column = params[:column]
|
70
|
+
|
71
|
+
# check against permitted columns
|
72
|
+
raise "Unpermitted column" unless ["column_a", "column_b"].include?(column)
|
73
|
+
|
74
|
+
User.top(column)
|
61
75
|
```
|
62
76
|
|
63
|
-
|
77
|
+
## Arrays and Hashes
|
64
78
|
|
65
|
-
|
66
|
-
|
79
|
+
Arrays
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
["up", "up", "down"].top
|
83
|
+
```
|
84
|
+
|
85
|
+
Hashes
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
{a: "up", b: "up", c: "down"}.top { |k, v| v }
|
89
|
+
```
|
90
|
+
|
91
|
+
Limit the results
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
["up", "up", "down"].top(1)
|
95
|
+
```
|
96
|
+
|
97
|
+
Include nil values
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
[nil, nil, "down"].top(nil: true)
|
101
|
+
```
|
102
|
+
|
103
|
+
Min count
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
["up", "up", "down"].top(min: 2)
|
67
107
|
```
|
68
108
|
|
69
109
|
## History
|
@@ -78,3 +118,12 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
|
|
78
118
|
- Fix bugs and [submit pull requests](https://github.com/ankane/hightop/pulls)
|
79
119
|
- Write, clarify, or fix documentation
|
80
120
|
- Suggest or add new features
|
121
|
+
|
122
|
+
To get started with development and testing:
|
123
|
+
|
124
|
+
```sh
|
125
|
+
git clone https://github.com/ankane/hightop.git
|
126
|
+
cd hightop
|
127
|
+
bundle install
|
128
|
+
bundle exec rake test
|
129
|
+
```
|
data/lib/hightop.rb
CHANGED
@@ -1,41 +1,17 @@
|
|
1
|
-
|
2
|
-
require "
|
3
|
-
|
4
|
-
module Hightop
|
5
|
-
def top(column, limit = nil, options = {})
|
6
|
-
if limit.is_a?(Hash)
|
7
|
-
options = limit
|
8
|
-
limit = nil
|
9
|
-
end
|
10
|
-
|
11
|
-
distinct = options[:distinct] || options[:uniq]
|
12
|
-
order_str = column.is_a?(Array) ? column.map(&:to_s).join(", ") : column
|
13
|
-
relation = group(column).order("count_#{distinct || 'all'} DESC, #{order_str}")
|
14
|
-
if limit
|
15
|
-
relation = relation.limit(limit)
|
16
|
-
end
|
1
|
+
# dependencies
|
2
|
+
require "active_support"
|
17
3
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
if options[:min]
|
25
|
-
relation = relation.having("COUNT(#{distinct ? "DISTINCT #{distinct}" : '*'}) >= #{options[:min].to_i}")
|
26
|
-
end
|
4
|
+
# modules
|
5
|
+
require "hightop/enumerable"
|
6
|
+
require "hightop/version"
|
27
7
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
else
|
33
|
-
relation.uniq.count(distinct)
|
34
|
-
end
|
35
|
-
else
|
36
|
-
relation.count
|
37
|
-
end
|
38
|
-
end
|
8
|
+
ActiveSupport.on_load(:active_record) do
|
9
|
+
require "hightop/utils"
|
10
|
+
require "hightop/kicks"
|
11
|
+
extend Hightop::Kicks
|
39
12
|
end
|
40
13
|
|
41
|
-
|
14
|
+
ActiveSupport.on_load(:mongoid) do
|
15
|
+
require "hightop/mongoid"
|
16
|
+
Mongoid::Document::ClassMethods.include(Hightop::Mongoid)
|
17
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Enumerable
|
2
|
+
def top(*args, **options, &block)
|
3
|
+
if block || !(respond_to?(:scoping) || respond_to?(:with_scope))
|
4
|
+
# TODO raise error if too many arguments
|
5
|
+
limit = args[0]
|
6
|
+
min = options[:min]
|
7
|
+
|
8
|
+
counts = Hash.new(0)
|
9
|
+
map(&block).each do |v|
|
10
|
+
counts[v] += 1
|
11
|
+
end
|
12
|
+
counts.delete(nil) unless options[:nil]
|
13
|
+
counts.select! { |_, v| v >= min } if min
|
14
|
+
|
15
|
+
arr = counts.sort_by { |_, v| -v }
|
16
|
+
arr = arr[0...limit] if limit
|
17
|
+
Hash[arr]
|
18
|
+
elsif respond_to?(:scoping)
|
19
|
+
scoping { @klass.send(:top, *args, **options, &block) }
|
20
|
+
else
|
21
|
+
with_scope(self) { klass.send(:top, *args, **options, &block) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Hightop
|
2
|
+
module Kicks
|
3
|
+
def top(column, limit = nil, distinct: nil, uniq: nil, min: nil, nil: nil)
|
4
|
+
warn "[hightop] uniq is deprecated. Use distinct instead" if uniq
|
5
|
+
|
6
|
+
columns = column.is_a?(Array) ? column : [column]
|
7
|
+
columns.each { |c| Utils.validate_column(c) }
|
8
|
+
|
9
|
+
distinct ||= uniq
|
10
|
+
Utils.validate_column(distinct) if distinct
|
11
|
+
|
12
|
+
relation = group(*columns).order("1 DESC", *columns)
|
13
|
+
if limit
|
14
|
+
relation = relation.limit(limit)
|
15
|
+
end
|
16
|
+
|
17
|
+
# terribly named option
|
18
|
+
unless binding.local_variable_get(:nil)
|
19
|
+
columns.each do |c|
|
20
|
+
c = Utils.resolve_column(self, c)
|
21
|
+
relation = relation.where("#{c} IS NOT NULL")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if min
|
26
|
+
if distinct
|
27
|
+
d = Utils.resolve_column(self, distinct)
|
28
|
+
relation = relation.having("COUNT(DISTINCT #{d}) >= #{min.to_i}")
|
29
|
+
else
|
30
|
+
relation = relation.having("COUNT(*) >= #{min.to_i}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if distinct
|
35
|
+
relation.distinct.count(distinct)
|
36
|
+
else
|
37
|
+
relation.count
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Hightop
|
2
|
+
module Mongoid
|
3
|
+
# super helpful article
|
4
|
+
# https://maximomussini.com/posts/mongoid-aggregation-dsl/
|
5
|
+
def top(column, limit = nil, distinct: nil, uniq: nil, min: nil, nil: nil)
|
6
|
+
warn "[hightop] uniq is deprecated. Use distinct instead" if uniq
|
7
|
+
|
8
|
+
columns = column.is_a?(Array) ? column : [column]
|
9
|
+
|
10
|
+
distinct ||= uniq
|
11
|
+
|
12
|
+
relation = all
|
13
|
+
|
14
|
+
# terribly named option
|
15
|
+
unless binding.local_variable_get(:nil)
|
16
|
+
columns.each do |c|
|
17
|
+
relation = relation.and(c.ne => nil)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
ids = {}
|
22
|
+
columns.each_with_index do |c, i|
|
23
|
+
ids["c#{i}"] = "$#{c}"
|
24
|
+
end
|
25
|
+
|
26
|
+
if distinct
|
27
|
+
# group with distinct column first, then group without it
|
28
|
+
# https://stackoverflow.com/questions/24761266/select-group-by-count-and-distinct-count-in-same-mongodb-query/24770233#24770233
|
29
|
+
distinct_ids = ids.merge("c#{ids.size}" => "$#{distinct}")
|
30
|
+
relation = relation.group(_id: distinct_ids, count: {"$sum" => 1})
|
31
|
+
ids.each_key do |k|
|
32
|
+
ids[k] = "$_id.#{k}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
relation = relation.group(_id: ids, count: {"$sum" => 1})
|
37
|
+
|
38
|
+
if min
|
39
|
+
relation.pipeline.push("$match" => {"count" => {"$gte" => min}})
|
40
|
+
end
|
41
|
+
|
42
|
+
relation = relation.desc(:count)
|
43
|
+
if limit
|
44
|
+
relation = relation.limit(limit)
|
45
|
+
end
|
46
|
+
|
47
|
+
result = {}
|
48
|
+
collection.aggregate(relation.pipeline).each do |doc|
|
49
|
+
key = doc["_id"].values
|
50
|
+
key = key[0] if key.size == 1
|
51
|
+
result[key] = doc["count"]
|
52
|
+
end
|
53
|
+
result
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hightop
|
2
|
+
module Utils
|
3
|
+
class << self
|
4
|
+
# basic version of Active Record disallow_raw_sql!
|
5
|
+
# symbol = column (safe), Arel node = SQL (safe), other = untrusted
|
6
|
+
# matches table.column and column
|
7
|
+
def validate_column(column)
|
8
|
+
unless column.is_a?(Symbol) || column.is_a?(Arel::Nodes::SqlLiteral) || /\A\w+(\.\w+)?\z/i.match(column.to_s)
|
9
|
+
warn "[hightop] Non-attribute argument: #{column}. Use Arel.sql() for known-safe values. This will raise an error in Hightop 0.3.0"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# resolves eagerly
|
14
|
+
def resolve_column(relation, column)
|
15
|
+
node = relation.send(:relation).send(:arel_columns, [column]).first
|
16
|
+
node = Arel::Nodes::SqlLiteral.new(node) if node.is_a?(String)
|
17
|
+
relation.connection.visitor.accept(node, Arel::Collectors::SQLString.new).value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/hightop/version.rb
CHANGED
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hightop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5'
|
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
|
-
version: '
|
26
|
+
version: '5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
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: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '5'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '5'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: sqlite3
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,25 +80,21 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
84
|
-
email:
|
85
|
-
- andrew@chartkick.com
|
83
|
+
description:
|
84
|
+
email: andrew@chartkick.com
|
86
85
|
executables: []
|
87
86
|
extensions: []
|
88
87
|
extra_rdoc_files: []
|
89
88
|
files:
|
90
|
-
- ".gitignore"
|
91
|
-
- ".travis.yml"
|
92
89
|
- CHANGELOG.md
|
93
|
-
- Gemfile
|
94
90
|
- LICENSE.txt
|
95
91
|
- README.md
|
96
|
-
- Rakefile
|
97
|
-
- hightop.gemspec
|
98
92
|
- lib/hightop.rb
|
93
|
+
- lib/hightop/enumerable.rb
|
94
|
+
- lib/hightop/kicks.rb
|
95
|
+
- lib/hightop/mongoid.rb
|
96
|
+
- lib/hightop/utils.rb
|
99
97
|
- lib/hightop/version.rb
|
100
|
-
- test/hightop_test.rb
|
101
|
-
- test/test_helper.rb
|
102
98
|
homepage: https://github.com/ankane/hightop
|
103
99
|
licenses:
|
104
100
|
- MIT
|
@@ -111,19 +107,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
107
|
requirements:
|
112
108
|
- - ">="
|
113
109
|
- !ruby/object:Gem::Version
|
114
|
-
version: '
|
110
|
+
version: '2.4'
|
115
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
112
|
requirements:
|
117
113
|
- - ">="
|
118
114
|
- !ruby/object:Gem::Version
|
119
115
|
version: '0'
|
120
116
|
requirements: []
|
121
|
-
|
122
|
-
rubygems_version: 2.4.5.1
|
117
|
+
rubygems_version: 3.1.2
|
123
118
|
signing_key:
|
124
119
|
specification_version: 4
|
125
120
|
summary: A nice shortcut for group count queries
|
126
|
-
test_files:
|
127
|
-
- test/hightop_test.rb
|
128
|
-
- test/test_helper.rb
|
129
|
-
has_rdoc:
|
121
|
+
test_files: []
|
data/.gitignore
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
*.gem
|
2
|
-
*.rbc
|
3
|
-
.bundle
|
4
|
-
.config
|
5
|
-
.yardoc
|
6
|
-
Gemfile.lock
|
7
|
-
InstalledFiles
|
8
|
-
_yardoc
|
9
|
-
coverage
|
10
|
-
doc/
|
11
|
-
lib/bundler/man
|
12
|
-
pkg
|
13
|
-
rdoc
|
14
|
-
spec/reports
|
15
|
-
test/tmp
|
16
|
-
test/version_tmp
|
17
|
-
tmp
|
18
|
-
*.bundle
|
19
|
-
*.so
|
20
|
-
*.o
|
21
|
-
*.a
|
22
|
-
mkmf.log
|
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
data/hightop.gemspec
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "hightop/version"
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "hightop"
|
8
|
-
spec.version = Hightop::VERSION
|
9
|
-
spec.authors = ["Andrew Kane"]
|
10
|
-
spec.email = ["andrew@chartkick.com"]
|
11
|
-
spec.summary = "A nice shortcut for group count queries"
|
12
|
-
spec.description = "A nice shortcut for group count queries"
|
13
|
-
spec.homepage = "https://github.com/ankane/hightop"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_dependency "activerecord"
|
22
|
-
|
23
|
-
spec.add_development_dependency "bundler", "~> 1.6"
|
24
|
-
spec.add_development_dependency "rake"
|
25
|
-
spec.add_development_dependency "minitest"
|
26
|
-
spec.add_development_dependency "sqlite3"
|
27
|
-
end
|
data/test/hightop_test.rb
DELETED
@@ -1,111 +0,0 @@
|
|
1
|
-
require_relative "test_helper"
|
2
|
-
|
3
|
-
class TestHightop < Minitest::Test
|
4
|
-
def setup
|
5
|
-
Visit.delete_all
|
6
|
-
end
|
7
|
-
|
8
|
-
def test_top
|
9
|
-
create_city("San Francisco", 3)
|
10
|
-
create_city("Chicago", 2)
|
11
|
-
expected = {
|
12
|
-
"San Francisco" => 3,
|
13
|
-
"Chicago" => 2
|
14
|
-
}
|
15
|
-
assert_equal expected, Visit.top(:city)
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_limit
|
19
|
-
create_city("San Francisco", 3)
|
20
|
-
create_city("Chicago", 2)
|
21
|
-
create_city("Boston", 1)
|
22
|
-
expected = {
|
23
|
-
"San Francisco" => 3,
|
24
|
-
"Chicago" => 2
|
25
|
-
}
|
26
|
-
assert_equal expected, Visit.top(:city, 2)
|
27
|
-
assert_equal expected, Visit.limit(2).top(:city)
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_nil_values
|
31
|
-
create_city("San Francisco", 3)
|
32
|
-
create_city(nil, 2)
|
33
|
-
expected = {
|
34
|
-
"San Francisco" => 3
|
35
|
-
}
|
36
|
-
assert_equal expected, Visit.top(:city)
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_nil_option
|
40
|
-
create_city("San Francisco", 3)
|
41
|
-
create_city(nil, 2)
|
42
|
-
expected = {
|
43
|
-
"San Francisco" => 3,
|
44
|
-
nil => 2
|
45
|
-
}
|
46
|
-
assert_equal expected, Visit.top(:city, nil: true)
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_multiple_groups
|
50
|
-
create_city("San Francisco")
|
51
|
-
expected = {
|
52
|
-
["San Francisco", "San Francisco"] => 1
|
53
|
-
}
|
54
|
-
assert_equal expected, Visit.top([:city, :city])
|
55
|
-
end
|
56
|
-
|
57
|
-
def test_expressions
|
58
|
-
create_city("San Francisco")
|
59
|
-
expected = {
|
60
|
-
"san francisco" => 1
|
61
|
-
}
|
62
|
-
assert_equal expected, Visit.top("LOWER(city)")
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_distinct
|
66
|
-
create(city: "San Francisco", user_id: 1)
|
67
|
-
create(city: "San Francisco", user_id: 1)
|
68
|
-
expected = {
|
69
|
-
"San Francisco" => 1
|
70
|
-
}
|
71
|
-
assert_equal expected, Visit.top(:city, distinct: :user_id)
|
72
|
-
end
|
73
|
-
|
74
|
-
def test_uniq
|
75
|
-
create(city: "San Francisco", user_id: 1)
|
76
|
-
create(city: "San Francisco", user_id: 1)
|
77
|
-
expected = {
|
78
|
-
"San Francisco" => 1
|
79
|
-
}
|
80
|
-
assert_equal expected, Visit.top(:city, uniq: :user_id)
|
81
|
-
end
|
82
|
-
|
83
|
-
def test_min
|
84
|
-
create_city("San Francisco", 3)
|
85
|
-
create_city("Chicago", 2)
|
86
|
-
expected = {
|
87
|
-
"San Francisco" => 3
|
88
|
-
}
|
89
|
-
assert_equal expected, Visit.top(:city, min: 3)
|
90
|
-
end
|
91
|
-
|
92
|
-
def test_min_distinct
|
93
|
-
create(city: "San Francisco", user_id: 1)
|
94
|
-
create(city: "San Francisco", user_id: 1)
|
95
|
-
create(city: "San Francisco", user_id: 2)
|
96
|
-
create(city: "Chicago", user_id: 1)
|
97
|
-
create(city: "Chicago", user_id: 1)
|
98
|
-
expected = {
|
99
|
-
"San Francisco" => 2
|
100
|
-
}
|
101
|
-
assert_equal expected, Visit.top(:city, min: 2, distinct: :user_id)
|
102
|
-
end
|
103
|
-
|
104
|
-
def create_city(city, count = 1)
|
105
|
-
create({city: city}, count)
|
106
|
-
end
|
107
|
-
|
108
|
-
def create(attributes, count = 1)
|
109
|
-
count.times { Visit.create!(attributes) }
|
110
|
-
end
|
111
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
require "bundler/setup"
|
2
|
-
Bundler.require(:default)
|
3
|
-
require "minitest/autorun"
|
4
|
-
require "minitest/pride"
|
5
|
-
require "logger"
|
6
|
-
|
7
|
-
Minitest::Test = Minitest::Unit::TestCase unless defined?(Minitest::Test)
|
8
|
-
|
9
|
-
# for debugging
|
10
|
-
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
11
|
-
|
12
|
-
# migrations
|
13
|
-
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
14
|
-
|
15
|
-
ActiveRecord::Migration.create_table :visits do |t|
|
16
|
-
t.string :city
|
17
|
-
t.string :user_id
|
18
|
-
end
|
19
|
-
|
20
|
-
class Visit < ActiveRecord::Base
|
21
|
-
end
|