hightop 0.2.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0b7e3f9ed74cce5d6a2e2a40f74634de04bcdcf8
4
- data.tar.gz: a4b20f5a241a447bac6b3d1eefc3123d93d076e2
2
+ SHA256:
3
+ metadata.gz: 7bfda8167d2141d8bd0be9678f1f285fe8417d0deecac5a04cee44f50efcf876
4
+ data.tar.gz: 0414c92e5702679fc169dbecb03d754e79461968e97a39cd2cca15d94ec4daa3
5
5
  SHA512:
6
- metadata.gz: 8a9fa28eac3b9273a8cb63f74031c1bfd6698dd409601ab4ccc8182b9eca4f8233e7e1d8de67053de8ae9a0bc621eee849f34e60addbba089a566ee698603eaf
7
- data.tar.gz: 046cb949463896d1ee5637dcb58529c5d6a9742c8f868fc6a2d85d252d353c6ccff2c8d0dd03e65e715c51d7d2347aaba309e18d1010171ac1b53f659ababcb1
6
+ metadata.gz: baad6fa1e6f4f404fd2f33448a971412ea9bf50257f5dd69531e341aa0a3afb69c816ac3191a9988ed8f30b19cce2c1d0837fb077e78fe589a7975b0283ec6d7
7
+ data.tar.gz: a66db10388aff6057d63f9ae977a700c0065d222a52dd04df0e7ea4bbd87f5ee51a145ff875aea5b9042dbbbaff2a10543f612519b3c65ec525852728aef5f82
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.2.1
2
+
3
+ - Added support for arrays and hashes
4
+
1
5
  ## 0.2.0
2
6
 
3
7
  - Use keyword arguments
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Andrew Kane
1
+ Copyright (c) 2014-2019 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  A nice shortcut for group count queries
4
4
 
5
- [![Build Status](https://travis-ci.org/ankane/hightop.svg)](https://travis-ci.org/ankane/hightop)
6
-
7
5
  ```ruby
8
6
  Visit.top(:browser)
9
7
  ```
@@ -14,7 +12,25 @@ instead of
14
12
  Visit.group(:browser).where("browser IS NOT NULL").order("count_all DESC, browser").count
15
13
  ```
16
14
 
17
- Be sure to [sanitize user input](http://rails-sqli.org/) like you must with `group`.
15
+ Be sure to [sanitize user input](https://rails-sqli.org/) like you must with `group`
16
+
17
+ Also works with arrays and hashes
18
+
19
+ ```ruby
20
+ ["up", "up", "down"].top(1)
21
+ ```
22
+
23
+ [![Build Status](https://travis-ci.org/ankane/hightop.svg)](https://travis-ci.org/ankane/hightop)
24
+
25
+ ## Installation
26
+
27
+ Add this line to your application’s Gemfile:
28
+
29
+ ```ruby
30
+ gem 'hightop'
31
+ ```
32
+
33
+ ## Options
18
34
 
19
35
  Limit the results
20
36
 
@@ -37,7 +53,7 @@ Visit.top([:city, :browser])
37
53
  And expressions
38
54
 
39
55
  ```ruby
40
- Visit.top("LOWER(referring_domain)")
56
+ Visit.top(Arel.sql("LOWER(referring_domain)"))
41
57
  ```
42
58
 
43
59
  And distinct
@@ -52,18 +68,36 @@ And min count
52
68
  Visit.top(:city, min: 10)
53
69
  ```
54
70
 
55
- ## Installation
71
+ ## Arrays and Hashes
56
72
 
57
- Add this line to your application’s Gemfile:
73
+ Arrays
58
74
 
59
75
  ```ruby
60
- gem 'hightop'
76
+ ["up", "up", "down"].top
61
77
  ```
62
78
 
63
- And then execute:
79
+ Hashes
80
+
81
+ ```ruby
82
+ {a: "up", b: "up", c: "down"}.top { |k, v| v }
83
+ ```
64
84
 
65
- ```sh
66
- bundle
85
+ Limit the results
86
+
87
+ ```ruby
88
+ ["up", "up", "down"].top(1)
89
+ ```
90
+
91
+ Include nil values
92
+
93
+ ```ruby
94
+ [nil, nil, "down"].top(nil: true)
95
+ ```
96
+
97
+ Min count
98
+
99
+ ```ruby
100
+ ["up", "up", "down"].top(min: 2)
67
101
  ```
68
102
 
69
103
  ## History
@@ -0,0 +1,26 @@
1
+ module Enumerable
2
+ def top(*args, &block)
3
+ if block || !respond_to?(:scoping)
4
+ limit, options, _ = args
5
+ if limit.is_a?(Hash) && args.size == 1
6
+ options = limit
7
+ limit = nil
8
+ end
9
+ options ||= {}
10
+ min = options[:min]
11
+
12
+ counts = Hash.new(0)
13
+ map(&block).each do |v|
14
+ counts[v] += 1
15
+ end
16
+ counts.delete(nil) unless options[:nil]
17
+ counts.select! { |_, v| v >= min } if min
18
+
19
+ arr = counts.sort_by { |_, v| -v }
20
+ arr = arr[0...limit] if limit
21
+ Hash[arr]
22
+ else
23
+ scoping { @klass.send(:top, *args, &block) }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module Hightop
2
+ module Kicks
3
+ def top(column, limit = nil, distinct: nil, uniq: nil, min: nil, nil: nil)
4
+ distinct ||= uniq
5
+ order_str = column.is_a?(Array) ? column.map(&:to_s).join(", ") : column
6
+ relation = group(column).order(["count_#{distinct || 'all'} DESC", order_str])
7
+ if limit
8
+ relation = relation.limit(limit)
9
+ end
10
+
11
+ # terribly named option
12
+ unless binding.local_variable_get(:nil)
13
+ (column.is_a?(Array) ? column : [column]).each do |c|
14
+ relation = relation.where("#{c} IS NOT NULL")
15
+ end
16
+ end
17
+
18
+ if min
19
+ relation = relation.having("COUNT(#{distinct ? "DISTINCT #{distinct}" : '*'}) >= #{min.to_i}")
20
+ end
21
+
22
+ if distinct
23
+ relation.distinct.count(distinct)
24
+ else
25
+ relation.count
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,3 @@
1
1
  module Hightop
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/lib/hightop.rb CHANGED
@@ -1,37 +1,11 @@
1
- require "hightop/version"
2
- require "active_record"
3
-
4
- module Hightop
5
- def top(column, limit = nil, distinct: nil, uniq: nil, min: nil, nil: nil)
6
- distinct ||= uniq
7
- order_str = column.is_a?(Array) ? column.map(&:to_s).join(", ") : column
8
- relation = group(column).order("count_#{distinct || 'all'} DESC, #{order_str}")
9
- if limit
10
- relation = relation.limit(limit)
11
- end
12
-
13
- # terribly named option
14
- unless binding.local_variable_get(:nil)
15
- (column.is_a?(Array) ? column : [column]).each do |c|
16
- relation = relation.where("#{c} IS NOT NULL")
17
- end
18
- end
1
+ # dependencies
2
+ require "active_support"
19
3
 
20
- if min
21
- relation = relation.having("COUNT(#{distinct ? "DISTINCT #{distinct}" : '*'}) >= #{min.to_i}")
22
- end
4
+ # modules
5
+ require "hightop/enumerable"
6
+ require "hightop/kicks"
7
+ require "hightop/version"
23
8
 
24
- if distinct
25
- # since relation.respond_to?(:distinct) can't be used
26
- if ActiveRecord::VERSION::MAJOR > 3
27
- relation.distinct.count(distinct)
28
- else
29
- relation.uniq.count(distinct)
30
- end
31
- else
32
- relation.count
33
- end
34
- end
9
+ ActiveSupport.on_load(:active_record) do
10
+ extend Hightop::Kicks
35
11
  end
36
-
37
- ActiveRecord::Base.send :extend, Hightop
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.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-20 00:00:00.000000000 Z
11
+ date: 2019-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '4.2'
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: '0'
26
+ version: '4.2'
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: '1.6'
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: '1.6'
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: '0'
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: '0'
68
+ version: '5'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: sqlite3
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,25 +80,19 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: A nice shortcut for group count queries
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
99
95
  - lib/hightop/version.rb
100
- - test/hightop_test.rb
101
- - test/test_helper.rb
102
96
  homepage: https://github.com/ankane/hightop
103
97
  licenses:
104
98
  - MIT
@@ -111,18 +105,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
105
  requirements:
112
106
  - - ">="
113
107
  - !ruby/object:Gem::Version
114
- version: '0'
108
+ version: '2.3'
115
109
  required_rubygems_version: !ruby/object:Gem::Requirement
116
110
  requirements:
117
111
  - - ">="
118
112
  - !ruby/object:Gem::Version
119
113
  version: '0'
120
114
  requirements: []
121
- rubyforge_project:
122
- rubygems_version: 2.6.8
115
+ rubygems_version: 3.0.4
123
116
  signing_key:
124
117
  specification_version: 4
125
118
  summary: A nice shortcut for group count queries
126
- test_files:
127
- - test/hightop_test.rb
128
- - test/test_helper.rb
119
+ 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
@@ -1,11 +0,0 @@
1
- language: ruby
2
- rvm: 2.2.5
3
- gemfile:
4
- - Gemfile
5
- sudo: false
6
- before_install: gem install bundler
7
- script: bundle exec rake test
8
- notifications:
9
- email:
10
- on_success: never
11
- on_failure: change
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in hightop.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
4
- task default: :test
5
- Rake::TestTask.new do |t|
6
- t.libs << "test"
7
- t.pattern = "test/**/*_test.rb"
8
- end
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,117 +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 test_bad_argument
105
- assert_raises(ArgumentError) do
106
- Visit.top(:city, boom: true)
107
- end
108
- end
109
-
110
- def create_city(city, count = 1)
111
- create({city: city}, count)
112
- end
113
-
114
- def create(attributes, count = 1)
115
- count.times { Visit.create!(attributes) }
116
- end
117
- 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