hightop 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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