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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 19538edc7ac8f8cc81df7ad682c47ec6dfe0d38e
4
- data.tar.gz: 2f2bb4d909ead9b64fa293627a784c5adeaadd2d
2
+ SHA256:
3
+ metadata.gz: e986b06b74104ea5fb99fe92abdb061eab4cc225ba9fa4f34216eadcd704e556
4
+ data.tar.gz: 5ca8954b8ce39c1212e0bc0b651e22450ff26b17795665b561cc7e4bbb134067
5
5
  SHA512:
6
- metadata.gz: ac01a53983e69a760c745bcddd99287774ae73d7b743dcf4be327b252c0524e3147b6e815a1560b735f6cdf2252d651402ea9b775468bd93300d056fdcea18d3
7
- data.tar.gz: 37252ef2688c355c2aa327f36fd5025c7809986abfc256bb4f8ea0df48442c8add698d944261b2a0ee0d16ee5c3d52b352023ad7c334a6ec39fe80e80a3922f0
6
+ metadata.gz: e9d93d7b9edcce9dcfb6f3b7912ef3252454731bcc68d45cc9ff391df854a6fe49e5d7ca269780e55e98cf1481a17ef3472427de69694d6098cfdcacf9e61c7d
7
+ data.tar.gz: cd86dfd59b77d39482d06d0a6e9f59869ca15eaab6eef6e5cc62f754ad9d3addd781e21749a192a181385620b2508b520ee22da1f2c397566ac82159778a65d2
@@ -1,37 +1,59 @@
1
- ## 0.1.4
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Andrew Kane
1
+ Copyright (c) 2014-2020 Andrew Kane
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -2,19 +2,28 @@
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)
7
+ # {
8
+ # "Chrome" => 63,
9
+ # "Safari" => 50,
10
+ # "Firefox" => 34
11
+ # }
9
12
  ```
10
13
 
11
- instead of
14
+ Works with Active Record, Mongoid, arrays and hashes
15
+
16
+ [![Build Status](https://travis-ci.org/ankane/hightop.svg?branch=master)](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
- Visit.group(:browser).where("browser IS NOT NULL").order("count_all DESC, browser").count
23
+ gem 'hightop'
15
24
  ```
16
25
 
17
- Be sure to [sanitize user input](http://rails-sqli.org/) like you must with `group`.
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
- ## Installation
64
+ ## User Input
56
65
 
57
- Add this line to your application’s Gemfile:
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
- gem 'hightop'
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
- And then execute:
77
+ ## Arrays and Hashes
64
78
 
65
- ```sh
66
- bundle
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
+ ```
@@ -1,41 +1,17 @@
1
- require "hightop/version"
2
- require "active_record"
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
- unless options[:nil]
19
- (column.is_a?(Array) ? column : [column]).each do |c|
20
- relation = relation.where("#{c} IS NOT NULL")
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
- if distinct
29
- # since relation.respond_to?(:distinct) can't be used
30
- if ActiveRecord::VERSION::MAJOR > 3
31
- relation.distinct.count(distinct)
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
- ActiveRecord::Base.send :extend, Hightop
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
@@ -1,3 +1,3 @@
1
1
  module Hightop
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.4"
3
3
  end
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.1.4
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: 2016-02-04 00:00:00.000000000 Z
11
+ date: 2020-09-07 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: '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: '0'
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: '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,21 @@ 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
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: '0'
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
- rubyforge_project:
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
@@ -1,12 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.1
4
- gemfile:
5
- - Gemfile
6
- sudo: false
7
- before_install: gem install bundler
8
- script: bundle exec rake test
9
- notifications:
10
- email:
11
- on_success: never
12
- 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
@@ -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
@@ -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
@@ -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