rails_view_counter 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 68f292167e4cdd05f97668402c13874c8430c72576617db238fed93300406b32
4
+ data.tar.gz: d548e125c5f634c57b6d52b1bba9f8a8d5985663f6c256921b61848fb64d03dc
5
+ SHA512:
6
+ metadata.gz: 47a42521d810b3100054ab638ec6f4567a753977d031cd9b0bd2231a7fe688c5defa58656291ff3dab09f7b48e23c24d3bad455b89241e2b12f3ccb5c5c7c58a
7
+ data.tar.gz: 0125db9783a615d06b5be8c0f039b910979b549def60f57ed75115241b16b1227019dd4dfe5ed0bd979717346435352d3fc5c16e8c24065b15a5fae7ebddaafe
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright aric.zheng
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # rails_view_counter
2
+ > A Rails gem for tracking and managing view counts with IP limiting.
3
+
4
+ ## Usage
5
+ ```sh
6
+ rails generate rails_view_counter:install
7
+ rails generate rails_view_counter:migration posts
8
+ rails db:migrate
9
+ ```
10
+ ```rb
11
+ # config/initializers/rails_view_counter.rb
12
+ RailsViewCounter.configure do |config|
13
+ config.ip_limit_duration = 2.hours
14
+ config.enable_ip_limit = true
15
+ config.log_views = true
16
+ end
17
+
18
+
19
+ class Post < ApplicationRecord
20
+ # 自动包含 ViewCounter::ModelAdditions
21
+ validates :title, presence: true
22
+ end
23
+ ```
24
+
25
+ ## Installation
26
+ Add this line to your application's Gemfile:
27
+
28
+ ```ruby
29
+ gem "rails_view_counter"
30
+ ```
31
+
32
+ And then execute:
33
+ ```bash
34
+ $ bundle
35
+ ```
36
+
37
+ Or install it yourself as:
38
+ ```bash
39
+ $ gem install rails_view_counter
40
+ ```
41
+
42
+ ## Resources
43
+ - https://chat.qwen.ai/c/b1a1ea17-4937-48df-abf0-81be548e3d89
44
+
45
+ ## Contributing
46
+ Contribution directions go here.
47
+
48
+ ## License
49
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ module RailsViewCounter
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module RailsViewCounter
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module RailsViewCounter
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module RailsViewCounter
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module RailsViewCounter
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Rails view counter</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "rails_view_counter/application", media: "all" %>
11
+ </head>
12
+ <body>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ RailsViewCounter::Engine.routes.draw do
2
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails/generators'
2
+
3
+ module RailsViewCounter
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+ desc 'Generates Rails View Counter initializer'
7
+
8
+ def copy_initializer
9
+ template 'initializer.rb', 'config/initializers/rails_view_counter.rb'
10
+ end
11
+
12
+ private
13
+
14
+ def source_paths
15
+ [File.expand_path('../templates', __dir__)]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module RailsViewCounter
5
+ class MigrationGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ source_root File.expand_path('../templates', __dir__)
9
+ desc 'Generates migration for adding view counter to a model'
10
+
11
+ argument :table_name, type: :string, required: true, desc: 'Table name to add view counter to'
12
+
13
+ def create_migration_file
14
+ # 确保表名是复数形式
15
+ plural_table_name = table_name.pluralize
16
+ migration_template 'migration.rb',
17
+ "db/migrate/add_view_counter_to_#{plural_table_name}.rb"
18
+ end
19
+
20
+ private
21
+
22
+ def source_paths
23
+ [File.expand_path('../templates', __dir__)]
24
+ end
25
+
26
+ # 必需的方法:返回下一个可用的迁移版本号
27
+ def self.next_migration_number(dirname)
28
+ next_migration_number = current_migration_number(dirname) + 1
29
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ # generators/templates/initializer.rb
2
+ RailsViewCounter.configure do |config|
3
+ # IP 限制时间间隔(默认1小时)
4
+ config.ip_limit_duration = 1.hour
5
+
6
+ # 是否启用 IP 限制
7
+ config.enable_ip_limit = true
8
+
9
+ # 是否记录详细访问日志
10
+ config.log_views = false
11
+
12
+ # 缓存存储方式
13
+ config.cache_store = :rails_cache
14
+ end
@@ -0,0 +1,6 @@
1
+ class AddViewCounterTo<%= table_name.camelize %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ add_column :<%= table_name %>, :view_count, :integer, default: 0
4
+ add_index :<%= table_name %>, :view_count
5
+ end
6
+ end
@@ -0,0 +1,21 @@
1
+ # lib/rails_view_counter/configuration.rb
2
+ module RailsViewCounter
3
+ def self.configuration
4
+ @configuration ||= Configuration.new
5
+ end
6
+
7
+ def self.configure
8
+ yield(configuration)
9
+ end
10
+
11
+ class Configuration
12
+ attr_accessor :ip_limit_duration, :enable_ip_limit, :log_views, :cache_store
13
+
14
+ def initialize
15
+ @ip_limit_duration = 1.hour
16
+ @enable_ip_limit = true
17
+ @log_views = false
18
+ @cache_store = :rails_cache
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ module RailsViewCounter
2
+ module ControllerAdditions
3
+ extend ActiveSupport::Concern
4
+
5
+ def increment_view_count!(viewable, options = {})
6
+ ip_address = get_user_ip
7
+ viewable.increment_view_count!(ip_address, options)
8
+ end
9
+
10
+ private
11
+
12
+ def get_user_ip
13
+ # 优先使用 X-Forwarded-For 头(用于负载均衡器或代理)
14
+ forwarded_ips = request.headers['X-Forwarded-For']
15
+ if forwarded_ips.present?
16
+ # 取第一个 IP(客户端真实 IP)
17
+ forwarded_ips.split(',').first.strip
18
+ else
19
+ # 直接使用 remote_ip
20
+ request.remote_ip
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ module RailsViewCounter
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsViewCounter
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ g.fixture_replacement :factory_bot
8
+ g.factory_bot dir: 'spec/factories'
9
+ end
10
+
11
+ initializer "rails_view_counter.active_record" do
12
+ ActiveSupport.on_load :active_record do
13
+ include RailsViewCounter::ModelAdditions
14
+ end
15
+ end
16
+
17
+ initializer "rails_view_counter.action_controller" do
18
+ ActiveSupport.on_load :action_controller do
19
+ include RailsViewCounter::ControllerAdditions
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,74 @@
1
+ # lib/rails_view_counter/model_additions.rb
2
+ module RailsViewCounter
3
+ module ModelAdditions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ after_initialize :set_default_view_count, if: :new_record?
8
+ end
9
+
10
+ def increment_view_count!(ip_address = nil, options = {})
11
+ return false unless view_counter_enabled?
12
+
13
+ if ip_address && should_count_view?(ip_address, options)
14
+ increment!(:view_count)
15
+ log_view(ip_address) if log_views_enabled?
16
+ true
17
+ else
18
+ false
19
+ end
20
+ end
21
+
22
+ def can_increment_view_count?(ip_address, options = {})
23
+ should_count_view?(ip_address, options)
24
+ end
25
+
26
+ private
27
+
28
+ def set_default_view_count
29
+ self.view_count ||= 0
30
+ end
31
+
32
+ def should_count_view?(ip_address, options = {})
33
+ return true unless ip_address
34
+
35
+ # 获取配置
36
+ limit_duration = options[:limit_duration] || RailsViewCounter.configuration.ip_limit_duration
37
+ enable_ip_limit = options[:enable_ip_limit] || RailsViewCounter.configuration.enable_ip_limit
38
+
39
+ return true unless enable_ip_limit
40
+
41
+ cache_key = generate_cache_key(ip_address)
42
+
43
+ # 检查该 IP 是否在限制时间内已经访问过
44
+ if Rails.cache.read(cache_key)
45
+ return false # 不允许增加浏览次数
46
+ else
47
+ # 设置缓存,有效期为限制时间
48
+ Rails.cache.write(cache_key, true, expires_in: limit_duration)
49
+ return true # 允许增加浏览次数
50
+ end
51
+ end
52
+
53
+ def generate_cache_key(ip_address)
54
+ "#{self.class.name.downcase}_#{id}_#{normalize_ip(ip_address)}"
55
+ end
56
+
57
+ def normalize_ip(ip)
58
+ ip.gsub(/[.:]/, '_')
59
+ end
60
+
61
+ def view_counter_enabled?
62
+ true
63
+ end
64
+
65
+ def log_views_enabled?
66
+ RailsViewCounter.configuration.log_views
67
+ end
68
+
69
+ def log_view(ip_address)
70
+ # 如果需要记录访问日志,这里可以实现
71
+ # ViewCounter::ViewLog.create!(...)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,3 @@
1
+ module RailsViewCounter
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ require "rails_view_counter/version"
2
+ require "rails_view_counter/engine"
3
+ require "rails_view_counter/model_additions"
4
+ require "rails_view_counter/controller_additions"
5
+ require "rails_view_counter/configuration"
6
+
7
+ module RailsViewCounter
8
+ class << self
9
+ attr_accessor :configuration
10
+ end
11
+
12
+ def self.configuration
13
+ @configuration ||= Configuration.new
14
+ end
15
+
16
+ def self.configure
17
+ yield(configuration)
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rails_view_counter do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_view_counter
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - aric.zheng
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-11-07 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: 6.0.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 6.0.0
26
+ description: A Rails gem for tracking and managing view counts with IP limiting and
27
+ cache optimization.
28
+ email:
29
+ - 1290657123@qq.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - app/assets/stylesheets/rails_view_counter/application.css
38
+ - app/controllers/rails_view_counter/application_controller.rb
39
+ - app/helpers/rails_view_counter/application_helper.rb
40
+ - app/jobs/rails_view_counter/application_job.rb
41
+ - app/mailers/rails_view_counter/application_mailer.rb
42
+ - app/models/rails_view_counter/application_record.rb
43
+ - app/views/layouts/rails_view_counter/application.html.erb
44
+ - config/routes.rb
45
+ - lib/generators/rails_view_counter/install_generator.rb
46
+ - lib/generators/rails_view_counter/migration_generator.rb
47
+ - lib/generators/templates/initializer.rb
48
+ - lib/generators/templates/migration.rb
49
+ - lib/rails_view_counter.rb
50
+ - lib/rails_view_counter/configuration.rb
51
+ - lib/rails_view_counter/controller_additions.rb
52
+ - lib/rails_view_counter/engine.rb
53
+ - lib/rails_view_counter/model_additions.rb
54
+ - lib/rails_view_counter/version.rb
55
+ - lib/tasks/rails_view_counter_tasks.rake
56
+ homepage: https://js.work
57
+ licenses:
58
+ - MIT
59
+ metadata:
60
+ homepage_uri: https://js.work
61
+ source_code_uri: https://github.com/ariczheng/rails_view_counter
62
+ changelog_uri: https://github.com/ariczheng/rails_view_counter/blob/main/CHANGELOG.md
63
+ allowed_push_host: https://rubygems.org
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.6.3
79
+ specification_version: 4
80
+ summary: A Rails gem for tracking and managing view counts with IP limiting.
81
+ test_files: []