ahoy-views 1.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7833368548e41179bc7ae7697fde7102958f3dfc65f6cd76b1cafeb485533626
4
+ data.tar.gz: 1c7bc67cb59b0dea5ffd44f3c6255e369ae4fbbafe0160e19297614c361df754
5
+ SHA512:
6
+ metadata.gz: abf402fecadcdb107dd623087b64649c43e7805c48fea51b3c2e86a70194ef17510eb9f529b47a4bbc142764749e9eb2ced9b785ec52acc3be33af06fe5f112d
7
+ data.tar.gz: 984ec5a5004c3a50fb2be4a25c35ce28824b1fec28553ff182720953dbeba262216584f328ccde01e74e9c5f90b61ddc3a4d4c209075090c4db61eea12906d96
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ### master
4
+
5
+ * nothing yet
6
+
7
+ ### 1.0.0 - 2018/01/13
8
+
9
+ * initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Jonas Hübotter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,209 @@
1
+ # Ahoy Views
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/ahoy-views.svg)](https://badge.fury.io/rb/ahoy-views) <img src="https://travis-ci.org/jonhue/ahoy-views.svg?branch=master" />
4
+
5
+ Track views of ActiveRecord objects in Rails. Ahoy Views depends on [Ahoy](https://github.com/ankane/ahoy).
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ * [Installation](#installation)
12
+ * [Usage](#usage)
13
+ * [Tracking](#tracking)
14
+ * [Types](#types)
15
+ * [`ahoy_viewable`](#ahoy_viewable)
16
+ * [`ahoy_viewer`](#ahoy_viewer)
17
+ * [To Do](#to-do)
18
+ * [Contributing](#contributing)
19
+ * [Contributors](#contributors)
20
+ * [Semantic versioning](#semantic-versioning)
21
+ * [License](#license)
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ **Note:** Before installing Ahoy Views make sure to setup the original [Ahoy](https://github.com/ankane/ahoy) gem.
28
+
29
+ Ahoy Views works with Rails 5 onwards. You can add it to your `Gemfile` with:
30
+
31
+ ```ruby
32
+ gem 'ahoy-views'
33
+ ```
34
+
35
+ And then execute:
36
+
37
+ $ bundle
38
+
39
+ Or install it yourself as:
40
+
41
+ $ gem install ahoy_views
42
+
43
+ If you always want to be up to date fetch the latest from GitHub in your `Gemfile`:
44
+
45
+ ```ruby
46
+ gem 'ahoy-views', github: 'jonhue/ahoy-views'
47
+ ```
48
+
49
+ Now run the generator:
50
+
51
+ $ rails g ahoy_views
52
+
53
+ Lastly make sure to call `ahoy_views` from your `Ahoy::Event` class:
54
+
55
+ ```ruby
56
+ module Ahoy
57
+ class Event < ApplicationRecord
58
+ ahoy_views
59
+ end
60
+ end
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Usage
66
+
67
+ ### Tracking
68
+
69
+ You can track views for ActiveRecord objects from your controllers:
70
+
71
+ ```ruby
72
+ class ArticleController < ApplicationController
73
+
74
+ def index
75
+ @articles = Article.all
76
+ ahoy_view @articles
77
+ end
78
+
79
+ def show
80
+ @article = Article.find params[:id]
81
+ ahoy_view @article
82
+ end
83
+
84
+ end
85
+ ```
86
+
87
+ **Note:** This will create an `Ahoy::Event` object for every record passed.
88
+
89
+ The `current_visit` object, if present, will be automatically associated with every visit.
90
+
91
+ You are also able to associate other records with views. [Learn more](#ahoy_viewer).
92
+
93
+ #### Types
94
+
95
+ You can pass an array of `types` to `ahoy_view`. Types allow you to track multiple types of visits. The `:types` option defaults to `[:visit]`.
96
+
97
+ ```ruby
98
+ ahoy_view @article, types: [:view, :visitor, :returnee, :unique_visitor, :unique_returnee]
99
+ ```
100
+
101
+ Here is a list of available types:
102
+
103
+ * `:visit` stores every call to `ahoy_view` for an object.
104
+
105
+ * `:visitor` stores only one call per visit to `ahoy_view`.
106
+
107
+ * `:returnee` ...
108
+
109
+ * `:unique_visitor` ...
110
+
111
+ * `:unique_returnee` ...
112
+
113
+ ### `ahoy_viewable`
114
+
115
+ Add `ahoy_viewable` to an ActiveRecord class.
116
+
117
+ ```ruby
118
+ class Article < ApplicationRecord
119
+ ahoy_viewable
120
+ end
121
+
122
+ a = Article.first
123
+
124
+ # All belonging Ahoy::Event objects that are a view
125
+ a.ahoy_views
126
+
127
+ # All viewer records that have a view object
128
+ a.ahoy_viewers
129
+
130
+ # Scope to order Article records by views
131
+ Article.trending
132
+
133
+ # Whether or not a is one of the 5 "most trending" articles
134
+ a.trending? 5
135
+ ```
136
+
137
+ ### `ahoy_viewer`
138
+
139
+ Add `ahoy_viewer` to an ActiveRecord class.
140
+
141
+ ```ruby
142
+ class User < ApplicationRecord
143
+ ahoy_viewer
144
+ end
145
+
146
+ u = User.first
147
+
148
+ # All belonging Ahoy::Event objects that are a view
149
+ u.ahoy_visits
150
+
151
+ # All records that this user has taken a look at
152
+ u.ahoy_viewed
153
+ ```
154
+
155
+ Here is how to associate a viewer with a view:
156
+
157
+ ```ruby
158
+ ahoy_view @article, viewer: current_user
159
+ ```
160
+
161
+ ---
162
+
163
+ ## To Do
164
+
165
+ [Here](https://github.com/jonhue/ahoy-views/projects/1) is the full list of current projects.
166
+
167
+ To propose your ideas, initiate the discussion by adding a [new issue](https://github.com/jonhue/ahoy-views/issues/new).
168
+
169
+ ---
170
+
171
+ ## Contributing
172
+
173
+ We hope that you will consider contributing to Ahoy Views. Please read this short overview for some information about how to get started:
174
+
175
+ [Learn more about contributing to this repository](CONTRIBUTING.md), [Code of Conduct](CODE_OF_CONDUCT.md)
176
+
177
+ ### Contributors
178
+
179
+ Give the people some :heart: who are working on this project. See them all at:
180
+
181
+ https://github.com/jonhue/ahoy-views/graphs/contributors
182
+
183
+ ### Semantic Versioning
184
+
185
+ Ahoy Views follows Semantic Versioning 2.0 as defined at http://semver.org.
186
+
187
+ ## License
188
+
189
+ MIT License
190
+
191
+ Copyright (c) 2018 Jonas Hübotter
192
+
193
+ Permission is hereby granted, free of charge, to any person obtaining a copy
194
+ of this software and associated documentation files (the "Software"), to deal
195
+ in the Software without restriction, including without limitation the rights
196
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
197
+ copies of the Software, and to permit persons to whom the Software is
198
+ furnished to do so, subject to the following conditions:
199
+
200
+ The above copyright notice and this permission notice shall be included in all
201
+ copies or substantial portions of the Software.
202
+
203
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
204
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
205
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
206
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
207
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
208
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
209
+ SOFTWARE.
@@ -0,0 +1,14 @@
1
+ require 'ahoy/views/version'
2
+
3
+ module Ahoy
4
+ module Views
5
+
6
+ autoload :View, 'ahoy/views/view'
7
+ autoload :Viewable, 'ahoy/views/viewable'
8
+ autoload :Viewer, 'ahoy/views/viewer'
9
+ autoload :Views, 'ahoy/views/views'
10
+
11
+ require 'ahoy/views/railtie'
12
+
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails/railtie'
2
+
3
+ module Ahoy
4
+ module Views
5
+ class Railtie < Rails::Railtie
6
+
7
+ initializer 'ahoy-views.active_record' do
8
+ ActiveSupport.on_load :active_record do
9
+ include Ahoy::Views::Views
10
+ include Ahoy::Views::Viewer
11
+ include Ahoy::Views::Viewable
12
+ end
13
+ end
14
+
15
+ initializer 'ahoy-views.action_controller' do
16
+ ActiveSupport.on_load :action_controller do
17
+ include Ahoy::Views::View
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ module Ahoy
2
+ module Views
3
+
4
+ VERSION = '1.0.0'
5
+
6
+ end
7
+ end
@@ -0,0 +1,35 @@
1
+ module Ahoy
2
+ module Views
3
+ module View
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ def ahoy_view objects, options = {}
8
+ defaults = {
9
+ types: [:view],
10
+ viewer: nil
11
+ }
12
+ options = defaults.merge! options
13
+
14
+ if current_visit
15
+ if objects.kind_of? Array
16
+ objects.each do |object|
17
+ track_ahoy_view object, options[:types], options[:viewer]
18
+ end
19
+ else
20
+ track_ahoy_view objects, options[:types], options[:viewer]
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def track_ahoy_view object, types, viewer
28
+ types.each do |name|
29
+ ahoy.track name, visited: object, visitor: viewer, visit: current_visit
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,30 @@
1
+ module Ahoy
2
+ module Views
3
+ module Viewable
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ def ahoy_viewable
10
+ has_many :ahoy_views, as: :visited, class_name: 'Ahoy::Event'
11
+ has_many :ahoy_viewers, through: :ahoy_views, source: :visitor
12
+
13
+ scope :trending, -> { left_joins(:views).group(:id).order('count(views.id) desc') }
14
+
15
+ include Ahoy::Views::Viewable::InstanceMethods
16
+ end
17
+
18
+ end
19
+
20
+ module InstanceMethods
21
+
22
+ def trending? limit
23
+ self.class.trending.limit(limit).include? self
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ module Ahoy
2
+ module Views
3
+ module Viewer
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ def ahoy_viewer
10
+ has_many :ahoy_visits, as: :visitor, class_name: 'Ahoy::Event'
11
+ has_many :ahoy_viewed, through: :ahoy_visits, source: :visited
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,50 @@
1
+ module Ahoy
2
+ module Views
3
+ module Views
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+
9
+ def ahoy_views
10
+ before_create :process_view
11
+
12
+ belongs_to :visitor, polymorphic: true, optional: true
13
+ belongs_to :visited, polymorphic: true, optional: true
14
+
15
+ scope :appearances, -> { where name: 'appearance' }
16
+ scope :views, -> { where name: 'view' }
17
+ scope :visitors, -> { where name: 'visitor' }
18
+ scope :returnees, -> { where name: 'returnee' }
19
+ scope :visited_in_session, -> (visit) { where visit_id: visit.id }
20
+ scope :visited, -> (visitor) { where visitor_id: visitor.id, visitor_type: visitor.class.name }
21
+
22
+ include Ahoy::Views::Views::InstanceMethods
23
+ end
24
+
25
+ end
26
+
27
+ module InstanceMethods
28
+
29
+ private
30
+
31
+ def process_view
32
+ if self.visited && self.name
33
+ case self.name
34
+ when 'visitor'
35
+ return false if self.class.visitors.visited_in_session(self.visit).where(visited_id: visited.id, visited_type: visited.class.name).any?
36
+ when 'returnee'
37
+ return false if self.class.returnees.visited_in_session(self.visit).where(visited_id: visited.id, visited_type: visited.class.name).any? && !self.class.visited(self.visitor).where(visited_id: visited.id, visited_type: visited.class.name).any?
38
+ when 'unique_visitor'
39
+ return false if self.class.visitors.visited(self.visitor).where(visited_id: visited.id, visited_type: visited.class.name).any?
40
+ when 'unique_returnee'
41
+ return false if self.class.returnees.visited(self.visitor).where(visited_id: visited.id, visited_type: visited.class.name).any? && !self.class.visited(self.visitor).where(visited_id: visited.id, visited_type: visited.class.name).any?
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class AhoyViewsGenerator < Rails::Generators::Base
5
+
6
+ include Rails::Generators::Migration
7
+
8
+ source_root File.join File.dirname(__FILE__), 'templates'
9
+ desc 'Install ahoy-views'
10
+
11
+ def self.next_migration_number dirname
12
+ if ActiveRecord::Base.timestamped_migrations
13
+ Time.now.utc.strftime '%Y%m%d%H%M%S'
14
+ else
15
+ "%.3d" % (current_migration_number(dirname) + 1)
16
+ end
17
+ end
18
+
19
+ def create_migration_file
20
+ migration_template 'migration.rb.erb', 'db/migrate/ahoy_views_migration.rb', migration_version: migration_version
21
+ end
22
+
23
+ def show_readme
24
+ readme 'README.md'
25
+ end
26
+
27
+ private
28
+
29
+ def migration_version
30
+ if Rails.version >= '5.0.0'
31
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1 @@
1
+ Now run `rails db:migrate` to add ahoy-views to your database.
@@ -0,0 +1,8 @@
1
+ class AhoyViewsMigration < ActiveRecord::Migration<%= migration_version %>
2
+ def change
3
+ add_column :ahoy_events, :visitor_id, :bigint, index: true
4
+ add_column :ahoy_events, :visitor_type, :string, index: true
5
+ add_column :ahoy_events, :visited_id, :bigint, index: true
6
+ add_column :ahoy_events, :visited_type, :string, index: true
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ahoy-views
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonas Hübotter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: actionpack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '5.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ahoy_matey
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.6'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.52'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.52'
111
+ description: Track views of ActiveRecord objects in Rails.
112
+ email: me@jonhue.me
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - CHANGELOG.md
118
+ - LICENSE
119
+ - README.md
120
+ - lib/ahoy-views.rb
121
+ - lib/ahoy/views/railtie.rb
122
+ - lib/ahoy/views/version.rb
123
+ - lib/ahoy/views/view.rb
124
+ - lib/ahoy/views/viewable.rb
125
+ - lib/ahoy/views/viewer.rb
126
+ - lib/ahoy/views/views.rb
127
+ - lib/generators/ahoy_views_generator.rb
128
+ - lib/generators/templates/README.md
129
+ - lib/generators/templates/migration.rb.erb
130
+ homepage: https://github.com/jonhue/ahoy-views
131
+ licenses:
132
+ - MIT
133
+ metadata: {}
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '2.3'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.7.4
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Track views of ActiveRecord objects in Rails
154
+ test_files: []