# Copyright (c) 2011 Roderick Monje
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
require 'rubygems'
require 'bundler/setup'
Bundler.require :default
class HitCounter
include Mongoid::Document
include Mongoid::Timestamps
# Mongo Config ===================================================================================
field :url
field :hits, :type => Integer, :default => 0
# Validates the HitCounter
's URL.
#
# If the URI library can parse the value and the scheme is valid, then we assume the url is valid.
class UrlValidator < ActiveModel::EachValidator
def validate_each record, attribute, value
begin
uri = Addressable::URI.parse value
if !['http', 'https'].include? uri.scheme
raise Addressable::URI::InvalidURIError
end
rescue Addressable::URI::InvalidURIError
record.errors[attribute] << 'Invalid URL'
end
end
end
validates :url, :presence => true, :url => true, :uniqueness => true
# Class methods ==================================================================================
# Returns a HitCounter
matching the specified URL. The HitCounter is created if no
# matching one is found. In the latter case, the hits argument specifies the starting count.
#
# * *Args*
# - +url+ -> the URL for the site being counted
# - +hits+ -> the number of hits to start with
# * *Returns*
# - the site's HitCounter
def self.get url, hits = 0
args = {:url => HitCounter.normalize_url(url)}
args[:hits] = hits unless HitCounter.where(:conditions => args).exists?
self.find_or_create_by args
end
# Instance methods: Overrides ====================================================================
# Sets the number of hits.
#
# * *Args*
# - +value+ -> the number of hits
# * *Returns*
# - the value
def hits= value
self[:hits] = value.to_i
end
# Sets the URL to be tracked. The http prefix is optional.
#
# * *Args*
# - +value+ -> the URL for the site being counted
# * *Returns*
# - the value
def url= value
self[:url] = HitCounter.normalize_url value
end
# Instance methods ===============================================================================
# Returns the hit count as a PNG image, using the specified style:
# 1 odometer
# 2 scout
# 3 Celtic
#
# * *Args*
# - +style_number+ -> the image style
# * *Returns*
# - the current hit count as a PNG image
def image style_number
image = HitCounter.cat_image self.hits.to_s, HitCounter.normalize_style_number(style_number)
image.format = 'png'
image
end
# Increments the hit count and saves the HitCounter.
#
# * *Returns*
# - true if the save was successful
def increment
self.hits += 1
self.save
end
private
STYLES = ['odometer', 'scout', 'celtic']
def self.cat_image number, style_index, images = Magick::ImageList.new
return images.append(false) if number.blank?
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
end
def self.normalize_style_number value
value = value.to_i
value -= 1 if value > 0
if value >= 0 && value < STYLES.size
return value
else
value % 3
end
end
def self.normalize_url value
value !~ %r{^http://} ? "http://#{value}" : value
end
end
if defined? Rails
class Railtie < Rails::Railtie
rake_tasks do
namespace :hit_counter do
desc 'Install HitCounter into your app.'
task :install do
puts 'Installing required image files...'
system "rsync -ruv #{Gem.searcher.find('hit_counter').full_gem_path}/config ."
system "rsync -ruv #{Gem.searcher.find('hit_counter').full_gem_path}/public ."
end
end
end
end
end