hit_counter 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +5 -13
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
  5. data/.github/ISSUE_TEMPLATE/task.md +21 -0
  6. data/.github/PULL_REQUEST_TEMPLATE.md +31 -0
  7. data/.github/dependabot.yml +11 -0
  8. data/.github/workflows/test.yml +35 -0
  9. data/.gitignore +2 -1
  10. data/.simplecov +15 -0
  11. data/CODE_OF_CONDUCT.md +10 -0
  12. data/CONTRIBUTING.md +34 -0
  13. data/Gemfile +4 -2
  14. data/LICENSE.txt +17 -11
  15. data/README.md +121 -0
  16. data/Rakefile +3 -1
  17. data/SECURITY.md +16 -0
  18. data/certs/ivanoblomov.pem +25 -0
  19. data/config/initializers/mime_types.rb +3 -1
  20. data/config/mongoid.yml +2 -2
  21. data/hit_counter.gemspec +29 -16
  22. data/lib/hit_counter.rb +77 -78
  23. data/lib/version.rb +4 -1
  24. data/public/images/digits/celtic/0.png +0 -0
  25. data/public/images/digits/celtic/1.png +0 -0
  26. data/public/images/digits/celtic/2.png +0 -0
  27. data/public/images/digits/celtic/3.png +0 -0
  28. data/public/images/digits/celtic/4.png +0 -0
  29. data/public/images/digits/celtic/5.png +0 -0
  30. data/public/images/digits/celtic/6.png +0 -0
  31. data/public/images/digits/celtic/7.png +0 -0
  32. data/public/images/digits/celtic/8.png +0 -0
  33. data/public/images/digits/celtic/9.png +0 -0
  34. data/public/images/digits/odometer/0.png +0 -0
  35. data/public/images/digits/odometer/1.png +0 -0
  36. data/public/images/digits/odometer/2.png +0 -0
  37. data/public/images/digits/odometer/3.png +0 -0
  38. data/public/images/digits/odometer/4.png +0 -0
  39. data/public/images/digits/odometer/5.png +0 -0
  40. data/public/images/digits/odometer/6.png +0 -0
  41. data/public/images/digits/odometer/7.png +0 -0
  42. data/public/images/digits/odometer/8.png +0 -0
  43. data/public/images/digits/odometer/9.png +0 -0
  44. data/public/images/digits/scout/0.png +0 -0
  45. data/public/images/digits/scout/1.png +0 -0
  46. data/public/images/digits/scout/2.png +0 -0
  47. data/public/images/digits/scout/3.png +0 -0
  48. data/public/images/digits/scout/4.png +0 -0
  49. data/public/images/digits/scout/5.png +0 -0
  50. data/public/images/digits/scout/6.png +0 -0
  51. data/public/images/digits/scout/7.png +0 -0
  52. data/public/images/digits/scout/8.png +0 -0
  53. data/public/images/digits/scout/9.png +0 -0
  54. data/spec/lib/hit_counter_spec.rb +97 -72
  55. data/spec/spec_helper.rb +5 -6
  56. data.tar.gz.sig +0 -0
  57. metadata +175 -74
  58. metadata.gz.sig +0 -0
  59. data/.ruby-version +0 -1
  60. data/.travis.yml +0 -4
  61. data/Gemfile.lock +0 -74
  62. data/README.rdoc +0 -84
  63. data/public/images/digits/celtic/0.gif +0 -0
  64. data/public/images/digits/celtic/1.gif +0 -0
  65. data/public/images/digits/celtic/2.gif +0 -0
  66. data/public/images/digits/celtic/3.gif +0 -0
  67. data/public/images/digits/celtic/4.gif +0 -0
  68. data/public/images/digits/celtic/5.gif +0 -0
  69. data/public/images/digits/celtic/6.gif +0 -0
  70. data/public/images/digits/celtic/7.gif +0 -0
  71. data/public/images/digits/celtic/8.gif +0 -0
  72. data/public/images/digits/celtic/9.gif +0 -0
  73. data/public/images/digits/odometer/0.gif +0 -0
  74. data/public/images/digits/odometer/1.gif +0 -0
  75. data/public/images/digits/odometer/2.gif +0 -0
  76. data/public/images/digits/odometer/3.gif +0 -0
  77. data/public/images/digits/odometer/4.gif +0 -0
  78. data/public/images/digits/odometer/5.gif +0 -0
  79. data/public/images/digits/odometer/6.gif +0 -0
  80. data/public/images/digits/odometer/7.gif +0 -0
  81. data/public/images/digits/odometer/8.gif +0 -0
  82. data/public/images/digits/odometer/9.gif +0 -0
  83. data/public/images/digits/scout/0.gif +0 -0
  84. data/public/images/digits/scout/1.gif +0 -0
  85. data/public/images/digits/scout/2.gif +0 -0
  86. data/public/images/digits/scout/3.gif +0 -0
  87. data/public/images/digits/scout/4.gif +0 -0
  88. data/public/images/digits/scout/5.gif +0 -0
  89. data/public/images/digits/scout/6.gif +0 -0
  90. data/public/images/digits/scout/7.gif +0 -0
  91. data/public/images/digits/scout/8.gif +0 -0
  92. data/public/images/digits/scout/9.gif +0 -0
data/lib/hit_counter.rb CHANGED
@@ -1,148 +1,147 @@
1
- # Copyright (c) 2011 Roderick Monje
2
- #
3
- # This program is free software: you can redistribute it and/or modify
4
- # it under the terms of the GNU General Public License as published by
5
- # the Free Software Foundation, either version 3 of the License, or
6
- # (at your option) any later version.
7
- #
8
- # This program is distributed in the hope that it will be useful,
9
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
- # GNU General Public License for more details.
12
- #
13
- # You should have received a copy of the GNU General Public License
14
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
1
+ # Copyright the HitCounter contributors.
2
+ # SPDX-License-Identifier: MIT
3
+ # frozen_string_literal: true
4
+
15
5
  require 'rubygems'
16
6
  require 'bundler/setup'
17
7
  Bundler.require :default
18
8
 
9
+ # Ruby version of that old 90s chestnut, <BLINK>the web-site hit counter</BLINK>
10
+ #
11
+ # @example
12
+ # hc = HitCounter.get 'cnn.com'
13
+ # hc.increment
19
14
  class HitCounter
20
15
  include Mongoid::Document
21
16
  include Mongoid::Timestamps
22
17
 
23
- # Mongo Config ===================================================================================
18
+ # Mongo Config ===============================================================
19
+ field :hits, type: Integer, default: 0
24
20
  field :url
25
- field :hits, :type => Integer, :default => 0
26
21
 
27
22
  # Validates the <code>HitCounter</code>'s URL.
28
23
  #
29
- # If the URI library can parse the value and the scheme is valid, then we assume the url is valid.
24
+ # If the URI library can parse the value and the scheme is valid, then we
25
+ # assume the url is valid.
30
26
  class UrlValidator < ActiveModel::EachValidator
31
- def validate_each record, attribute, value
32
- begin
33
- uri = Addressable::URI.parse value
34
-
35
- if !['http', 'https'].include? uri.scheme
36
- raise Addressable::URI::InvalidURIError
37
- end
38
- rescue Addressable::URI::InvalidURIError
39
- record.errors[attribute] << 'Invalid URL'
40
- end
27
+ def validate_each(record, attribute, value)
28
+ uri = Addressable::URI.parse value
29
+ raise Addressable::URI::InvalidURIError unless %w[http https].include?(
30
+ uri.scheme
31
+ )
32
+ rescue Addressable::URI::InvalidURIError
33
+ record.errors.add attribute, 'Invalid URL'
41
34
  end
42
35
  end
43
36
 
44
- validates :url, :presence => true, :url => true, :uniqueness => true
37
+ validates :hits, numericality: { only_integer: true }
38
+ validates :url, presence: true, url: true, uniqueness: true
45
39
 
46
- # Class methods ==================================================================================
40
+ # Class methods ==============================================================
47
41
 
48
- # Returns a <code>HitCounter</code> matching the specified URL. The HitCounter is created if no
49
- # matching one is found. In the latter case, the hits argument specifies the starting count.
42
+ # Returns a <code>HitCounter</code> matching the specified URL. The HitCounter
43
+ # is created if no matching one is found. In the latter case, the hits
44
+ # argument specifies the starting count.
50
45
  #
51
- # * *Args*
52
- # - +url+ -> the URL for the site being counted
53
- # - +hits+ -> the number of hits to start with
54
- # * *Returns*
55
- # - the site's HitCounter
56
- def self.get url, hits = 0
57
- args = {:url => HitCounter.normalize_url(url)}
58
- args[:hits] = hits unless HitCounter.where(:conditions => args).exists?
59
- self.find_or_create_by args
46
+ # @param url [String] the URL for the site being counted
47
+ # @param hits [Integer] the number of hits to start with
48
+ # @return [HitCounter] the site's HitCounter
49
+ # @example
50
+ # hc = HitCounter.get 'cnn.com'
51
+ # hc = HitCounter.get 'cnn.com', 100
52
+ def self.get(url, hits = 0)
53
+ args = { url: normalize_url(url) }
54
+ args[:hits] = hits unless where(conditions: args).exists?
55
+ find_or_create_by args
60
56
  end
61
57
 
62
- # Instance methods: Overrides ====================================================================
58
+ # Instance methods: Overrides ================================================
63
59
 
64
60
  # Sets the number of hits.
65
61
  #
66
- # * *Args*
67
- # - +value+ -> the number of hits
68
- # * *Returns*
69
- # - the value
70
- def hits= value
62
+ # @param value [Integer] the initial number of hits
63
+ # @return [Integer] the number of hits
64
+ # @example
65
+ # hc.hits = 100
66
+ def hits=(value)
71
67
  self[:hits] = value.to_i
72
68
  end
73
69
 
74
70
  # Sets the URL to be tracked. The http prefix is optional.
75
71
  #
76
- # * *Args*
77
- # - +value+ -> the URL for the site being counted
78
- # * *Returns*
79
- # - the value
80
- def url= value
81
- self[:url] = HitCounter.normalize_url value
72
+ # @param value [String] the URL for the site being counted
73
+ # @return [String] the normalized URL
74
+ # @example
75
+ # hc.url = 'cnn.com'
76
+ def url=(value)
77
+ self[:url] = HitCounter.send(:normalize_url, value)
82
78
  end
83
79
 
84
- # Instance methods ===============================================================================
80
+ # Instance methods ===========================================================
85
81
 
86
82
  # Returns the hit count as a PNG image, using the specified style:
87
83
  # 1 odometer
88
84
  # 2 scout
89
85
  # 3 Celtic
90
86
  #
91
- # * *Args*
92
- # - +style_number+ -> the image style
93
- # * *Returns*
94
- # - the current hit count as a PNG image
95
- def image style_number
96
- image = HitCounter.cat_image self.hits.to_s, HitCounter.normalize_style_number(style_number)
87
+ # @param style_number [String] the image style
88
+ # @return [Magick::Image] the current hit count as a PNG image
89
+ # @example
90
+ # hc.image = 1
91
+ def image(style_number)
92
+ image = HitCounter.send(:cat_image, hits.to_s, HitCounter.send(:normalize_style_number, style_number))
97
93
  image.format = 'png'
98
94
  image
99
95
  end
100
96
 
101
97
  # Increments the hit count and saves the HitCounter.
102
98
  #
103
- # * *Returns*
104
- # - true if the save was successful
99
+ # @return [true] if the save was successful
100
+ # @example
101
+ # hc.increment
105
102
  def increment
106
103
  self.hits += 1
107
- self.save
104
+ save
108
105
  end
109
106
 
110
- private
111
-
112
- STYLES = ['odometer', 'scout', 'celtic']
107
+ STYLES = %w[odometer scout celtic].freeze
113
108
 
114
- def self.cat_image number, style_index, images = Magick::ImageList.new
109
+ private_class_method def self.cat_image(number, style_index, images = Magick::ImageList.new)
115
110
  return images.append(false) if number.blank?
116
- HitCounter.cat_image number[1..-1], style_index, images << Magick::Image.read("#{Rails.root}/public/images/digits/#{STYLES[style_index]}/#{number[0..0]}.gif").first
111
+
112
+ cat_image(number[1..-1], style_index, images <<
113
+ Magick::Image.read("#{Rails.root}/public/images/digits/"\
114
+ "#{STYLES[style_index]}/#{number[0..0]}.png").first)
117
115
  end
118
116
 
119
- def self.normalize_style_number value
117
+ private_class_method def self.normalize_style_number(value)
120
118
  value = value.to_i
121
- value -= 1 if value > 0
119
+ value -= 1 if value.positive?
122
120
 
123
- if value >= 0 && value < STYLES.size
124
- return value
125
- else
126
- value % 3
127
- end
121
+ return value if value >= 0 && value < STYLES.size
122
+
123
+ value % 3
128
124
  end
129
125
 
130
- def self.normalize_url value
126
+ private_class_method def self.normalize_url(value)
131
127
  value !~ %r{^http://} ? "http://#{value}" : value
132
128
  end
133
129
  end
134
130
 
135
131
  if defined? Rails
132
+ # Overriding Rails to include install task
136
133
  class Railtie < Rails::Railtie
137
134
  rake_tasks do
138
135
  namespace :hit_counter do
139
136
  desc 'Install HitCounter into your app.'
140
137
  task :install do
141
138
  puts 'Installing required image files...'
142
- system "rsync -ruv #{Gem.searcher.find('hit_counter').full_gem_path}/config ."
143
- system "rsync -ruv #{Gem.searcher.find('hit_counter').full_gem_path}/public ."
139
+ system "rsync -ruv #{Gem.searcher.find('hit_counter').full_gem_path}"\
140
+ '/config .'
141
+ system "rsync -ruv #{Gem.searcher.find('hit_counter').full_gem_path}"\
142
+ '/public .'
144
143
  end
145
144
  end
146
145
  end
147
146
  end
148
- end
147
+ end
data/lib/version.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ruby version of that old 90s chestnut, <BLINK>the web-site hit counter</BLINK>
1
4
  class HitCounter
2
- VERSION = '0.1.3'.freeze unless defined?(HitCounter::VERSION)
5
+ VERSION = '0.1.4' unless defined?(HitCounter::VERSION)
3
6
  end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,118 +1,143 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ # Copyright the HitCounter contributors.
2
+ # SPDX-License-Identifier: MIT
3
+ # frozen_string_literal: true
2
4
 
5
+ require "#{File.dirname(__FILE__)}/../spec_helper"
6
+
7
+ JAVASCRIPT_HACK = %q(eval("console.log('All your base belong to us!')"))
8
+ # rubocop:disable Metrics/BlockLength
3
9
  describe HitCounter do
4
- before :all do
5
- HitCounter.delete_all
6
- @hit_counter = HitCounter.get 'www.google.com'
7
- end
10
+ before(:all) { described_class.delete_all }
11
+
12
+ subject { described_class.get 'www.google.com' }
8
13
 
9
- describe '#get' do
10
- context 'when existing URL' do
11
- before { @hit_counter2 = HitCounter.get @hit_counter.url, 10 }
14
+ describe '.get' do
15
+ context 'with existing URL' do
16
+ let!(:hit_counter2) { described_class.get subject.url, 10 }
12
17
 
13
- describe 'document count' do
14
- it { expect(HitCounter.count).to eq 1 }
18
+ describe '.count' do
19
+ it { expect(described_class.count).to eq 1 }
15
20
  end
16
21
 
17
22
  context 'with starting count 10' do
18
- describe 'hits' do
19
- it { expect(@hit_counter2.hits).to eq 10 }
23
+ describe '#hits' do
24
+ it { expect(hit_counter2.hits).to eq 10 }
20
25
  end
21
26
  end
22
27
  end
28
+ context 'with new URL' do
29
+ before { described_class.get 'www.cnn.com' }
23
30
 
24
- context 'when new URL' do
25
- before { HitCounter.get 'www.cnn.com' }
26
-
27
- describe 'document count' do
28
- it { expect(HitCounter.count).to eq 2 }
31
+ describe '.count' do
32
+ it { expect(described_class.count).to eq 2 }
29
33
  end
30
34
 
31
35
  context 'with blank starting count' do
32
36
  describe '#hits' do
33
- it { expect(HitCounter.get('www.nytimes.com', nil).hits).to eq 0 }
37
+ it do
38
+ expect(described_class.get('www.nytimes.com', nil).hits).to eq 0
39
+ end
34
40
  end
35
41
  end
36
-
37
42
  context 'with starting count 10' do
38
43
  describe '#hits' do
39
- it { expect(HitCounter.get('online.wsj.com', 10).hits).to eq 10 }
44
+ it { expect(described_class.get('online.wsj.com', 10).hits).to eq 10 }
40
45
  end
41
46
  end
42
47
  end
43
- end
48
+ context 'with JavaScript' do
49
+ before { described_class.delete_all }
44
50
 
45
- describe '#hits' do
46
- subject { @hit_counter.hits }
51
+ subject { described_class.get(JAVASCRIPT_HACK) }
47
52
 
48
- it { expect(subject).to eq 0 }
53
+ it do
54
+ subject.save
55
+ expect(subject).to be_invalid
56
+ end
57
+ end
58
+ end
59
+ describe '#hits' do
60
+ it { expect(subject.hits).to eq 0 }
49
61
 
50
62
  context 'when incremented' do
51
- before { @hit_counter.increment }
63
+ before { subject.increment }
52
64
 
53
- it { expect(@hit_counter.hits).to eq 1 }
65
+ it { expect(subject.hits).to eq 1 }
54
66
  end
55
67
  end
68
+ describe '#hits=' do
69
+ context 'with JavaScript' do
70
+ before { subject.hits = JAVASCRIPT_HACK }
56
71
 
57
- describe '#image' do
58
- subject { @hit_counter.image '1' }
59
-
60
- it { expect(subject).to be_a Magick::Image }
72
+ it { expect(subject.hits).to eq 0 }
73
+ end
61
74
  end
62
-
63
- describe '#normalize_style_number' do
64
- context 'when nil' do
65
- it { expect(HitCounter.normalize_style_number(nil)).to be_zero }
75
+ describe '#image' do
76
+ context 'with a valid :style_number' do
77
+ it { expect(subject.image('1').filename).to eq "./public/images/digits/odometer/#{subject.hits}.png" }
66
78
  end
67
-
68
- context 'when empty string' do
69
- it { expect(HitCounter.normalize_style_number('')).to be_zero }
79
+ context 'with JavaScript' do
80
+ it {
81
+ expect(subject.image(JAVASCRIPT_HACK).filename).to eq "./public/images/digits/odometer/#{subject.hits}.png"
82
+ }
70
83
  end
71
-
72
- context 'when within range' do
73
- it 'returns 0 for 1' do
74
- expect(HitCounter.normalize_style_number('1')).to eq 0
84
+ end
85
+ describe '#url=' do
86
+ context 'with JavaScript' do
87
+ before do
88
+ subject.url = JAVASCRIPT_HACK
89
+ subject.save
75
90
  end
76
91
 
77
- it 'returns 2 for 3' do
78
- expect(HitCounter.normalize_style_number('3')).to eq 2
79
- end
92
+ it { expect(subject).to be_invalid }
80
93
  end
81
-
82
- context 'when outside range' do
83
- context 'with negative' do
84
- context 'returns 2 for' do
85
- it -1 do
86
- expect(HitCounter.normalize_style_number('-1')).to eq 2
87
- end
88
-
89
- it -31 do
90
- expect(HitCounter.normalize_style_number('-31')).to eq 2
94
+ end
95
+ describe '.normalize_style_number' do
96
+ context 'with nil' do
97
+ it { expect(described_class.send(:normalize_style_number, nil)).to be_zero }
98
+ end
99
+ context 'with empty string' do
100
+ it { expect(described_class.send(:normalize_style_number, '')).to be_zero }
101
+ end
102
+ context 'with JavaScript' do
103
+ it { expect(described_class.send(:normalize_style_number, JAVASCRIPT_HACK)).to be_zero }
104
+ end
105
+ context 'with number in range' do
106
+ {
107
+ '1' => 0,
108
+ '3' => 2
109
+ }.each do |input, output|
110
+ context "with '#{input}'" do
111
+ it do
112
+ expect(described_class.send(:normalize_style_number, input)).to eq output
91
113
  end
92
114
  end
93
115
  end
94
-
95
- context 'with positive' do
96
- context 'returns 0 for' do
97
- it 4 do
98
- expect(HitCounter.normalize_style_number('4')).to eq 0
99
- end
100
-
101
- it 34 do
102
- expect(HitCounter.normalize_style_number('34')).to eq 0
116
+ end
117
+ context 'with number outside range' do
118
+ {
119
+ '-31' => 2,
120
+ '-1' => 2,
121
+ '4' => 0,
122
+ '34' => 0
123
+ }.each do |input, output|
124
+ context "with '#{input}'" do
125
+ it do
126
+ expect(described_class.send(:normalize_style_number, input)).to eq output
103
127
  end
104
128
  end
105
129
  end
106
130
  end
107
131
  end
108
-
109
- describe '#normalize_url' do
110
- context 'with "cnn.com"' do
111
- it { expect(HitCounter.normalize_url('cnn.com')).to eq 'http://cnn.com' }
112
- end
113
-
114
- context 'with "http://www.nytimes.com"' do
115
- it { expect(HitCounter.normalize_url('http://www.nytimes.com')).to eq 'http://www.nytimes.com' }
132
+ describe '.normalize_url' do
133
+ {
134
+ 'cnn.com' => 'http://cnn.com',
135
+ 'http://www.nytimes.com' => 'http://www.nytimes.com'
136
+ }.each do |input, output|
137
+ context "with '#{input}'" do
138
+ it { expect(described_class.send(:normalize_url, input)).to eq output }
139
+ end
116
140
  end
117
141
  end
118
- end
142
+ end
143
+ # rubocop:enable Metrics/BlockLength
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,8 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
- $LOAD_PATH.unshift(File.dirname(__FILE__))
1
+ # frozen_string_literal: true
3
2
 
4
3
  require 'simplecov'
5
- SimpleCov.start
6
-
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
6
  require 'addressable/uri'
8
7
  require 'mongoid'
9
8
  require 'RMagick'
@@ -12,11 +11,11 @@ require 'rspec'
12
11
  require 'hit_counter'
13
12
 
14
13
  Mongoid.load! 'config/mongoid.yml', :test
15
- Mongoid.logger = false
14
+ Mongo::Logger.logger.level = ::Logger::WARN
16
15
 
17
16
  # Stub Rails root during tests.
18
17
  module Rails
19
18
  def self.root
20
19
  '.'
21
20
  end
22
- end
21
+ end
data.tar.gz.sig ADDED
Binary file