notches 0.3.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
+ *.gem
2
+ .bundle
3
+ .sass-cache
4
+ Gemfile.lock
5
+ pkg/*
6
+ *.sqlite
7
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/HISTORY ADDED
@@ -0,0 +1,2 @@
1
+ 0.1.0 - ?
2
+ * Initial Release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Hyper Tiny
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.
@@ -0,0 +1,54 @@
1
+ Notches
2
+ -------
3
+
4
+ A Rails Engine for tracking your web traffic.
5
+
6
+ Installation
7
+ ------------
8
+
9
+ Add this to your Gemfile and run the `bundle` command.
10
+
11
+ gem 'notches', '~> 0.1.0'
12
+
13
+ And then install and run the necessary migrations.
14
+
15
+ rake notches:install:migrations
16
+ rake db:migrate
17
+
18
+ Mount your engine at your desired location in `config/routes.rb`.
19
+
20
+ mount Notches::Engine => '/notches'
21
+
22
+ Finally to start recording hits include the notch pixel image at the bottom of your views.
23
+
24
+ <%= image_tag "/notches/hits/new.gif?url=#{request.url}" %>
25
+
26
+ Counting hits
27
+ -------------
28
+
29
+ For a URL:
30
+
31
+ Notches::Hit.joins(:url).where('url like ?', '/posts').count
32
+
33
+ For an IP:
34
+
35
+ Notches::Hit.joins(:ip).where('ip = ?', '127.0.0.1').count
36
+
37
+ For a session id:
38
+
39
+ Notches::Hit.joins(:session).where('session_id = ?', 'abcd').count
40
+
41
+ For a date:
42
+
43
+ Notches::Hit.joins(:date).where('date = ?', Date.today).count
44
+
45
+ For a particular time of day:
46
+
47
+ Notches::Hit.joins(:time).where('time between ?', '09:00:00', '17:00:00').count
48
+
49
+ Or a user agent:
50
+
51
+ Notches::Hit.joins(:user_agent).where('user_agent like ?', '%Mobile%').count
52
+
53
+ To get a better idea of how Notches is setup check out the
54
+ [Notches::Hit](http://github.com/hypertiny/notches/blob/master/app/models/notches/hit.rb) model.
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ require 'rspec/core/rake_task'
8
+ RSpec::Core::RakeTask.new
9
+
10
+ task :default => :test
11
+ task :test => :spec
@@ -0,0 +1,18 @@
1
+ class Notches::HitsController < ActionController::Base
2
+ def new
3
+ session[:notches]
4
+ Notches::Hit.log(
5
+ :url => params[:url],
6
+ :user_agent => request.env["HTTP_USER_AGENT"],
7
+ :session_id => request.session_options[:id],
8
+ :ip => request.remote_ip
9
+ )
10
+
11
+ # Don't cache the image.
12
+ response.header["Last-Modified"] = Time.now.httpdate
13
+
14
+ send_file Notches::Engine.root.join("app/assets/images/notches/hit.gif"),
15
+ :type => "image/gif",
16
+ :disposition => "inline"
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ class Notches::Date < ActiveRecord::Base
2
+ self.table_name = "notches_dates"
3
+
4
+ has_many :hits, :class_name => "Notches::Hit",
5
+ :foreign_key => 'notches_date_id'
6
+ end
@@ -0,0 +1,37 @@
1
+ class Notches::Hit < ActiveRecord::Base
2
+ self.table_name = "notches_hits"
3
+
4
+ validates :url, :presence => true
5
+ validates :session, :presence => true
6
+ validates :ip, :presence => true
7
+
8
+ validates_associated :url, :session, :ip
9
+
10
+ belongs_to :url, :foreign_key => :notches_url_id, :class_name => "URL"
11
+ belongs_to :user_agent, :foreign_key => :notches_user_agent_id
12
+ belongs_to :session, :foreign_key => :notches_session_id
13
+ belongs_to :ip, :foreign_key => :notches_ip_id, :class_name => "IP"
14
+ belongs_to :date, :foreign_key => :notches_date_id
15
+ belongs_to :time, :foreign_key => :notches_time_id
16
+
17
+ scope :unique, group("notches_url_id, notches_session_id, notches_ip_id")
18
+
19
+ def self.log(attributes)
20
+ hit = self.new
21
+ hit.transaction do
22
+ Rails.logger.info("[Notches] Tracking #{attributes.inspect} at #{Date.today} #{Time.now}")
23
+ hit.url = Notches::URL.find_or_create_by_url(attributes[:url])
24
+ hit.user_agent = Notches::UserAgent.find_or_create_by_user_agent(attributes[:user_agent])
25
+ hit.session = Notches::Session.find_or_create_by_session_id(attributes[:session_id])
26
+ hit.ip = Notches::IP.find_or_create_by_ip(attributes[:ip])
27
+ hit.date = Notches::Date.find_or_create_by_date(Date.today)
28
+ hit.time = Notches::Time.find_or_create_by_time(Time.now)
29
+ begin
30
+ hit.save!
31
+ rescue #ActiveRecord::RecordNotUnique
32
+ Rails.logger.info "Skipping non-unique hit"
33
+ end
34
+ end
35
+ hit
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ class Notches::IP < ActiveRecord::Base
2
+ self.table_name = "notches_ips"
3
+
4
+ has_many :hits, :class_name => "Notches::Hit",
5
+ :foreign_key => 'notches_ip_id'
6
+
7
+ validates :ip, :presence => true, :allow_blank => false
8
+ end
@@ -0,0 +1,8 @@
1
+ class Notches::Session < ActiveRecord::Base
2
+ self.table_name = "notches_sessions"
3
+
4
+ has_many :hits, :class_name => "Notches::Hit",
5
+ :foreign_key => 'notches_session_id'
6
+
7
+ validates :session_id, :presence => true
8
+ end
@@ -0,0 +1,6 @@
1
+ class Notches::Time < ActiveRecord::Base
2
+ self.table_name = "notches_times"
3
+
4
+ has_many :hits, :class_name => "Notches::Hit",
5
+ :foreign_key => 'notches_time_id'
6
+ end
@@ -0,0 +1,8 @@
1
+ class Notches::URL < ActiveRecord::Base
2
+ self.table_name = "notches_urls"
3
+
4
+ has_many :hits, :class_name => "Notches::Hit",
5
+ :foreign_key => 'notches_url_id'
6
+
7
+ validates :url, :presence => true
8
+ end
@@ -0,0 +1,5 @@
1
+ class Notches::UserAgent < ActiveRecord::Base
2
+ self.table_name = "notches_user_agents"
3
+
4
+ has_many :hits, :class_name => "Notches::Hit"
5
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize!
7
+ run Combustion::Application
@@ -0,0 +1,3 @@
1
+ Notches::Engine.routes.draw do
2
+ get '/hits/new' => 'hits#new', :as => :new_hit
3
+ end
@@ -0,0 +1,55 @@
1
+ class CreateNotches < ActiveRecord::Migration
2
+ def up
3
+ create_table :notches_urls do |t|
4
+ t.string :url
5
+ end
6
+ create_table :notches_sessions do |t|
7
+ t.string :session_id
8
+ end
9
+ create_table :notches_ips do |t|
10
+ t.string :ip
11
+ end
12
+ create_table :notches_dates do |t|
13
+ t.date :date
14
+ end
15
+ create_table :notches_times do |t|
16
+ t.time :time
17
+ end
18
+ create_table :notches_hits do |t|
19
+ t.integer :notches_url_id, :null => false
20
+ t.integer :notches_session_id, :null => false
21
+ t.integer :notches_ip_id, :null => false
22
+ t.integer :notches_date_id, :null => false
23
+ t.integer :notches_time_id, :null => false
24
+ end
25
+ add_index :notches_urls, :url
26
+ add_index :notches_sessions, :session_id
27
+ add_index :notches_ips, :ip
28
+ add_index :notches_dates, :date
29
+ add_index :notches_times, :time
30
+ add_index :notches_hits, [
31
+ :notches_time_id,
32
+ :notches_date_id,
33
+ :notches_ip_id,
34
+ :notches_session_id,
35
+ :notches_url_id
36
+ ], :name => :notches_index, :unique => true
37
+ end
38
+
39
+ def down
40
+ remove_index :notches_urls, :url
41
+ remove_index :notches_sessions, :session_id
42
+ remove_index :notches_ips, :ip
43
+ remove_index :notches_dates, :date
44
+ remove_index :notches_times, :time
45
+ change_table :notches_hits do |t|
46
+ t.remove_index :name => :notches_index
47
+ end
48
+ drop_table :notches_urls
49
+ drop_table :notches_sessions
50
+ drop_table :notches_ips
51
+ drop_table :notches_dates
52
+ drop_table :notches_times
53
+ drop_table :notches_hits
54
+ end
55
+ end
@@ -0,0 +1,34 @@
1
+ class CreateNotchesUserAgents < ActiveRecord::Migration
2
+ def up
3
+ create_table :notches_user_agents do |t|
4
+ t.text :user_agent
5
+ end
6
+ add_column :notches_hits, :notches_user_agent_id, :integer
7
+ change_table :notches_hits do |t|
8
+ t.remove_index :name => :notches_index
9
+ end
10
+ add_index :notches_hits, [
11
+ :notches_time_id,
12
+ :notches_date_id,
13
+ :notches_ip_id,
14
+ :notches_session_id,
15
+ :notches_url_id,
16
+ :notches_user_agent_id
17
+ ], :name => :notches_index, :unique => true
18
+ end
19
+
20
+ def down
21
+ change_table :notches_hits do |t|
22
+ t.remove_index :name => :notches_index
23
+ end
24
+ add_index :notches_hits, [
25
+ :notches_time_id,
26
+ :notches_date_id,
27
+ :notches_ip_id,
28
+ :notches_session_id,
29
+ :notches_url_id
30
+ ], :name => :notches_index, :unique => true
31
+ remove_column :notches_hits, :notches_user_agent_id
32
+ drop_table :notches_user_agents
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ require 'rails'
2
+ require 'notches/engine'
3
+ require 'notches/version'
@@ -0,0 +1,5 @@
1
+ module Notches
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Notches
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Notches
2
+ VERSION = '0.3.0'
3
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "notches/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'notches'
7
+ s.version = Notches::VERSION
8
+ s.authors = ["Cillian O'Ruanaidh", "Paul Campbell"]
9
+ s.email = ['contact@cilliano.com']
10
+ s.homepage = 'http://github.com/hypertiny/notches'
11
+ s.summary = %q{Simple Rails web traffic counter}
12
+ s.description = %q{A Rails Engine for tracking your web traffic.}
13
+
14
+ s.rubyforge_project = 'notches'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f|
19
+ File.basename(f)
20
+ }
21
+ s.require_paths = ['lib']
22
+
23
+ s.add_runtime_dependency 'rails', '~> 3.1'
24
+
25
+ s.add_development_dependency 'combustion', '~> 0.3.2'
26
+ s.add_development_dependency 'rspec-rails', '~> 2.11'
27
+ s.add_development_dependency 'sqlite3', '~> 1.3.4'
28
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Notches::HitsController do
4
+ describe "#new" do
5
+ it "returns an image" do
6
+ get :new, :url => '/posts', :format => 'gif'
7
+
8
+ response.headers["Content-Type"].should == 'image/gif'
9
+ end
10
+
11
+ it "logs the hit" do
12
+ @request.env['HTTP_USER_AGENT'] = 'FeedBurner/1.0'
13
+ @request.stub(:remote_ip => '234.101.82.14')
14
+ @request.stub(:session_options => { :id => 'abcd' })
15
+
16
+ get :new, :url => '/posts', :format => 'gif'
17
+
18
+ Notches::Hit.count.should == 1
19
+ hit = Notches::Hit.first
20
+ hit.url.url.should == '/posts'
21
+ hit.user_agent.user_agent.should == 'FeedBurner/1.0'
22
+ hit.ip.ip.should == '234.101.82.14'
23
+ hit.session.session_id.should == 'abcd'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/notches_test.sqlite
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+ namespace 'notches' do
3
+ resources :hits, :only => [:new]
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ ActiveRecord::Schema.define do
2
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ describe Notches::Hit do
4
+ describe "validations" do
5
+ let(:hit) do
6
+ Notches::Hit.new(
7
+ :url => Notches::URL.new(:url => "/posts"),
8
+ :session => Notches::Session.new(:session_id => '1234'),
9
+ :ip => Notches::IP.new(:ip => '209.85.143.94')
10
+ )
11
+ end
12
+
13
+ its "valid with valid attributes" do
14
+ hit.should be_valid
15
+ end
16
+
17
+ it "requires an ip" do
18
+ hit.ip = nil
19
+ hit.should have(1).error_on(:ip)
20
+ end
21
+
22
+ it "requires a session" do
23
+ hit.session = nil
24
+ hit.should have(1).error_on(:session)
25
+ end
26
+
27
+ it "requires a " do
28
+ hit.url = nil
29
+ hit.should have(1).error_on(:url)
30
+ end
31
+
32
+ it "does not validate if its IP's ip blank" do
33
+ hit.ip = Notches::IP.new(:ip => '')
34
+ hit.should have(1).error_on(:ip)
35
+ end
36
+
37
+ it "does not validate if its Session's session_id is blank" do
38
+ hit.session = Notches::Session.new(:session_id => '')
39
+ hit.should have(1).error_on(:session)
40
+ end
41
+
42
+ it "does not validate if its URL's url is blank" do
43
+ hit.url = Notches::URL.new(:url => '')
44
+ hit.should have(1).error_on(:url)
45
+ end
46
+ end
47
+
48
+ describe ".log" do
49
+
50
+ context "with valid attributes" do
51
+ let(:today) { Date.today }
52
+ let(:now) { Time.now }
53
+ before do
54
+ Date.stub(:today => today)
55
+ Time.stub(:now => now)
56
+ end
57
+
58
+ it "creates a hit for today and current time with those attributes" do
59
+ Notches::Hit.log({
60
+ :url => '/posts',
61
+ :user_agent => 'FeedBurner/1.0',
62
+ :session_id => '1',
63
+ :ip => '0.0.0.1'
64
+ })
65
+ hit = Notches::Hit.first
66
+ hit.should be_present
67
+ hit.url.url.should == '/posts'
68
+ hit.session.session_id.should == '1'
69
+ hit.ip.ip.should == '0.0.0.1'
70
+ hit.date.date.should == today
71
+ hit.time.time.strftime('%H:%M:%S').should == now.strftime('%H:%M:%S')
72
+ hit.user_agent.user_agent.should == 'FeedBurner/1.0'
73
+ end
74
+ end
75
+
76
+ let(:existing_hit) {
77
+ Notches::Hit.log(
78
+ :url => "/posts/1",
79
+ :user_agent => 'FeedBurner/1.0',
80
+ :session_id => '1',
81
+ :ip => '0.0.0.1'
82
+ )
83
+ }
84
+
85
+ it "does not create a new IP record if there's a record for that IP already" do
86
+ hit = Notches::Hit.log(
87
+ :url => "/posts/2",
88
+ :session_id => '2',
89
+ :ip => existing_hit.ip.ip
90
+ )
91
+ hit.ip.id.should == existing_hit.ip.id
92
+ end
93
+
94
+ it "does not create a new URL record if there's a record for that URL already" do
95
+ hit = Notches::Hit.log(
96
+ :url => existing_hit.url.url,
97
+ :session_id => '2',
98
+ :ip => '0.0.0.2'
99
+ )
100
+ hit.url.id.should == existing_hit.url.id
101
+ end
102
+
103
+ it "does not create a new Session record there's a record for that session id already" do
104
+ hit = Notches::Hit.log(
105
+ :url => "/posts/2",
106
+ :session_id => existing_hit.session.session_id,
107
+ :ip => '0.0.0.2'
108
+ )
109
+ hit.session.id.should == existing_hit.session.id
110
+ end
111
+
112
+ it "does not create a new Date record there's a record for that Date already" do
113
+ hit = Notches::Hit.log(
114
+ :url => '/posts/2',
115
+ :session_id => '2',
116
+ :ip => '0.0.0.2'
117
+ )
118
+ hit.date.id.should == existing_hit.date.id
119
+ end
120
+
121
+ it "does not create a new Time record there's a record for that Time already" do
122
+ Time.stub(:now => existing_hit.time.time)
123
+ hit = Notches::Hit.log(
124
+ :url => '/posts/2',
125
+ :session_id => '2',
126
+ :ip => '0.0.0.2'
127
+ )
128
+ hit.time.id.should == existing_hit.time.id
129
+ end
130
+
131
+ it "does not create a new UserAgent record if there's a record for that UserAgent already" do
132
+ hit = Notches::Hit.log(
133
+ :url => '/post/2',
134
+ :user_agent => 'FeedBurner/1.0',
135
+ :session_id => '2',
136
+ :ip => '0.0.0.2'
137
+ )
138
+ hit.user_agent.id.should == existing_hit.user_agent.id
139
+ end
140
+
141
+ context "a hit with the same url, session, ip, date and time exists already" do
142
+ it "does not log the hit" do
143
+ Date.stub(:today => existing_hit.date.date)
144
+ Time.stub(:now => existing_hit.time.time)
145
+ expect {
146
+ hit = Notches::Hit.log(
147
+ :url => existing_hit.url.url,
148
+ :user_agent => 'FeedBurner/1.0',
149
+ :session_id => existing_hit.session.session_id,
150
+ :ip => existing_hit.ip.ip
151
+ )
152
+ }.to_not change { Notches::Hit.count }
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize!
7
+
8
+ require 'rspec/rails'
9
+
10
+ RSpec.configure do |config|
11
+ config.use_transactional_fixtures = true
12
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: notches
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Cillian O'Ruanaidh
9
+ - Paul Campbell
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-07-22 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '3.1'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '3.1'
31
+ - !ruby/object:Gem::Dependency
32
+ name: combustion
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: 0.3.2
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 0.3.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec-rails
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.11'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '2.11'
63
+ - !ruby/object:Gem::Dependency
64
+ name: sqlite3
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ version: 1.3.4
71
+ type: :development
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: 1.3.4
79
+ description: A Rails Engine for tracking your web traffic.
80
+ email:
81
+ - contact@cilliano.com
82
+ executables: []
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - HISTORY
89
+ - LICENSE
90
+ - README.md
91
+ - Rakefile
92
+ - app/assets/images/notches/hit.gif
93
+ - app/controllers/notches/hits_controller.rb
94
+ - app/models/notches/date.rb
95
+ - app/models/notches/hit.rb
96
+ - app/models/notches/ip.rb
97
+ - app/models/notches/session.rb
98
+ - app/models/notches/time.rb
99
+ - app/models/notches/url.rb
100
+ - app/models/notches/user_agent.rb
101
+ - config.ru
102
+ - config/routes.rb
103
+ - db/migrate/20120323111734_create_notches.rb
104
+ - db/migrate/20120713142636_create_notches_user_agents.rb
105
+ - lib/notches.rb
106
+ - lib/notches/engine.rb
107
+ - lib/notches/version.rb
108
+ - notches.gemspec
109
+ - spec/controllers/notches/hits_controller_spec.rb
110
+ - spec/internal/config/database.yml
111
+ - spec/internal/config/routes.rb
112
+ - spec/internal/db/schema.rb
113
+ - spec/internal/log/.gitignore
114
+ - spec/internal/public/favicon.ico
115
+ - spec/models/notches/hit_spec.rb
116
+ - spec/spec_helper.rb
117
+ homepage: http://github.com/hypertiny/notches
118
+ licenses: []
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ segments:
130
+ - 0
131
+ hash: 1181156043935805657
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ segments:
139
+ - 0
140
+ hash: 1181156043935805657
141
+ requirements: []
142
+ rubyforge_project: notches
143
+ rubygems_version: 1.8.24
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Simple Rails web traffic counter
147
+ test_files:
148
+ - spec/controllers/notches/hits_controller_spec.rb
149
+ - spec/internal/config/database.yml
150
+ - spec/internal/config/routes.rb
151
+ - spec/internal/db/schema.rb
152
+ - spec/internal/log/.gitignore
153
+ - spec/internal/public/favicon.ico
154
+ - spec/models/notches/hit_spec.rb
155
+ - spec/spec_helper.rb