monocle 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,45 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ spec/db/*.sqlite3
18
+ Gemfile.lock
19
+
20
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
21
+ #
22
+ # * Create a file at ~/.gitignore
23
+ # * Include files you want ignored
24
+ # * Run: git config --global core.excludesfile ~/.gitignore
25
+ #
26
+ # After doing this, these files will be ignored in all your git projects,
27
+ # saving you from having to 'pollute' every project you touch with them
28
+ #
29
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
30
+ #
31
+ # For MacOS:
32
+ #
33
+ .DS_Store
34
+ #
35
+ # For TextMate
36
+ #*.tmproj
37
+ #tmtags
38
+ #
39
+ # For emacs:
40
+ #*~
41
+ #\#*
42
+ #.\#*
43
+ #
44
+ # For vim:
45
+ #*.swp
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
data/Gemfile CHANGED
@@ -1,20 +1,18 @@
1
- source "http://rubygems.org"
2
- # Add dependencies required to use your gem here.
3
- # Example:
4
- # gem "activesupport", ">= 2.3.5"
1
+ source 'http://rubygems.org'
5
2
 
6
- # Add dependencies to develop your gem here.
7
- # Include everything needed to run rake, tests, features, etc.
8
- group :development do
9
- gem "bundler", "~> 1.0.0"
10
- gem "database_cleaner"
11
- gem "jeweler", "~> 1.5.2"
12
- gem "mocha"
13
- gem "rails"
14
- gem "rcov", ">= 0"
15
- gem "rspec", "~> 2.3.0"
16
- gem "sinatra"
17
- gem "sqlite3"
18
- gem "shoulda-matchers", "1.0.0.beta1"
19
- gem "yard", "~> 0.6.0"
3
+ gem 'activesupport'
4
+ gem 'redis'
5
+
6
+ group :development, :test do
7
+ gem 'rails'
8
+ gem 'sinatra'
9
+ gem 'yard'
10
+ gem 'guard'
11
+ gem 'guard-rspec'
12
+ gem 'terminal-notifier-guard'
13
+ gem 'rb-fsevent', '~> 0.9.1'
14
+ end
15
+
16
+ group :test do
17
+ gem 'rspec'
20
18
  end
@@ -0,0 +1,6 @@
1
+ guard :rspec, version: 2, all_after_pass: true, all_on_start: false, keep_failed: false, cli: '--tty --color --format nested' do
2
+ watch(%r{^spec/.+_spec\.rb})
3
+ watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { 'spec' }
6
+ end
@@ -1,3 +1,5 @@
1
+ {<img src="https://secure.travis-ci.org/russ/Monocle.png?branch=master" alt="Build Status" />}[http://travis-ci.org/russ/Monocle]
2
+
1
3
  = monocle
2
4
 
3
5
  Gemfile
data/Rakefile CHANGED
@@ -1,43 +1,15 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
1
+ require 'bundler/gem_tasks'
10
2
  require 'rake'
11
-
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "monocle"
16
- gem.homepage = "http://github.com/russ/monocle"
17
- gem.license = "MIT"
18
- gem.summary = %Q{A history of view events.}
19
- gem.description = %Q{A history of view events.}
20
- gem.email = "russ@bashme.org"
21
- gem.authors = ["Russ Smith"]
22
- # Include your dependencies below. Runtime dependencies are required when using your gem,
23
- # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
- # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
- # gem.add_development_dependency 'rspec', '> 1.2.3'
26
- end
27
- Jeweler::RubygemsDotOrgTasks.new
28
-
29
- require 'rspec/core'
30
3
  require 'rspec/core/rake_task'
31
- RSpec::Core::RakeTask.new(:spec) do |spec|
32
- spec.pattern = FileList['spec/**/*_spec.rb']
33
- end
34
4
 
35
- RSpec::Core::RakeTask.new(:rcov) do |spec|
36
- spec.pattern = 'spec/**/*_spec.rb'
37
- spec.rcov = true
5
+ namespace :spec do
6
+ RSpec::Core::RakeTask.new(:normal) do |t|
7
+ t.pattern ='spec/**/*_spec.rb'
8
+ t.rcov = false
9
+ end
38
10
  end
39
11
 
40
- task :default => :spec
12
+ desc "RSpec tests"
13
+ task "spec" => "spec:normal"
41
14
 
42
- require 'yard'
43
- YARD::Rake::YardocTask.new
15
+ task "default" => "spec"
@@ -1,11 +1,79 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/core_ext'
3
+ require 'redis'
4
+
5
+ require 'monocle/core_ext'
6
+
1
7
  module Monocle
2
- autoload :Server, "monocle/server"
3
- autoload :Views, "monocle/views"
4
-
5
- autoload :View, "monocle/view"
6
- autoload :OverallView, "monocle/overall_view"
7
- autoload :DailyView, "monocle/daily_view"
8
- autoload :WeeklyView, "monocle/weekly_view"
9
- autoload :MonthlyView, "monocle/monthly_view"
10
- autoload :YearlyView, "monocle/yearly_view"
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ class_attribute :_monocle_options,
12
+ :_monocle_view_types,
13
+ :_monocle_redis_connection
14
+
15
+ self._monocle_options = {
16
+ cache_view_counts: false
17
+ }
18
+
19
+ self._monocle_view_types = {
20
+ overall: -> { 'overall' },
21
+ yearly: -> { Time.now.beginning_of_year },
22
+ monthly: -> { Time.now.beginning_of_month },
23
+ weekly: -> { Time.now.beginning_of_week },
24
+ daily: -> { Time.now.beginning_of_day },
25
+ hourly: -> { Time.now.beginning_of_hour }
26
+ }
27
+
28
+ self._monocle_redis_connection = Redis.new || REDIS
29
+ end
30
+
31
+ module ClassMethods
32
+ def monocle_key(*append)
33
+ extra = (append.empty?) ? '' : ':' + append.join(':')
34
+ "monocle:#{self.to_s.underscore}" + extra
35
+ end
36
+
37
+ def monocle_options(options = {})
38
+ self._monocle_options = self._monocle_options.merge(options)
39
+ end
40
+
41
+ def monocle_views(types = {})
42
+ self._monocle_view_types = types
43
+ self._monocle_view_types.each do |k,v|
44
+ define_method("#{k}_views_count") do
45
+ self._monocle_redis_connection.hget(self.class.monocle_key(id), self.send("#{k}_views_field")).to_i || 0
46
+ end
47
+
48
+ define_method("#{k}_views_field") do
49
+ field = v.call
50
+ field.is_a?(String) ? field : field.to_i
51
+ end
52
+ end
53
+ end
54
+
55
+ def most_viewed_since(since, limit = 1000)
56
+ objects = self._monocle_redis_connection.zrevrangebyscore(self.monocle_key, Time.now.to_i, since.to_i, limit: [0, limit])
57
+ objects.collect { |o| self.find(o[0]) }
58
+ end
59
+ end
60
+
61
+ def view!
62
+ self._monocle_view_types.keys.each do |view_type|
63
+ cache_field = "#{view_type}_views".to_sym
64
+ count = self._monocle_redis_connection.hincrby(self.class.monocle_key(id), self.send("#{view_type}_views_field"), 1)
65
+ if self._monocle_options[:cache_view_counts] && respond_to?(cache_field)
66
+ update_column(cache_field, count) if respond_to?(:update_column)
67
+ set(cache_field, count) if respond_to?(:set)
68
+ end
69
+ end
70
+
71
+ self._monocle_redis_connection.zadd(self.class.monocle_key, Time.now.to_i, id)
72
+ end
73
+
74
+ def destroy_views
75
+ self._monocle_redis_connection.del(self.class.monocle_key(id))
76
+ end
77
+
78
+ autoload :Server, 'monocle/server'
11
79
  end
@@ -0,0 +1,5 @@
1
+ class Time
2
+ def beginning_of_hour
3
+ change(min: 0, sec: 0, usec: 0)
4
+ end
5
+ end
@@ -1,14 +1,13 @@
1
- require "sinatra/base"
1
+ require 'sinatra/base'
2
2
 
3
3
  module Monocle
4
4
  class Server < Sinatra::Base
5
- post "/:type/:id(.:format)" do
6
- begin
7
- params[:type].classify.constantize.find(params[:id]).view!
8
- ActiveRecord::Base.clear_active_connections!
9
- "o_0 +1"
10
- rescue ActiveRecord::RecordNotFound
11
- "o_0"
5
+ post '/:type/:id(.:format)' do
6
+ if object = params[:type].classify.constantize.find(params[:id])
7
+ object.view!
8
+ 'o_0 +1'
9
+ else
10
+ 'o_0'
12
11
  end
13
12
  end
14
13
  end
@@ -0,0 +1,3 @@
1
+ module Monocle
2
+ VERSION = '0.2.0'
3
+ end
@@ -1,101 +1,23 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "monocle/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
6
  s.name = %q{monocle}
8
- s.version = "0.1.1"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Russ Smith"]
7
+ s.version = Monocle::VERSION
8
+ s.authors = ["Russ Smith (russ@bashme.org)"]
12
9
  s.date = %q{2011-08-31}
13
10
  s.description = %q{A history of view events.}
14
11
  s.email = %q{russ@bashme.org}
15
- s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- ".rspec",
22
- "Gemfile",
23
- "LICENSE.txt",
24
- "README.rdoc",
25
- "Rakefile",
26
- "VERSION",
27
- "lib/generators/monocle/install_generator.rb",
28
- "lib/generators/monocle/templates/migration.rb",
29
- "lib/monocle.rb",
30
- "lib/monocle/daily_view.rb",
31
- "lib/monocle/monthly_view.rb",
32
- "lib/monocle/overall_view.rb",
33
- "lib/monocle/server.rb",
34
- "lib/monocle/view.rb",
35
- "lib/monocle/views.rb",
36
- "lib/monocle/weekly_view.rb",
37
- "lib/monocle/yearly_view.rb",
38
- "monocle.gemspec",
39
- "spec/db/migrate/20110502201938_create_viewables.rb",
40
- "spec/db/migrate/20110502223022_create_monocle_views.rb",
41
- "spec/spec_helper.rb",
42
- "spec/view_spec.rb",
43
- "spec/viewable_spec.rb",
44
- "transparent.gif"
45
- ]
46
12
  s.homepage = %q{http://github.com/russ/monocle}
47
13
  s.licenses = ["MIT"]
48
14
  s.require_paths = ["lib"]
49
15
  s.rubygems_version = %q{1.6.2}
50
16
  s.summary = %q{A history of view events.}
51
- s.test_files = [
52
- "spec/db/migrate/20110502201938_create_viewables.rb",
53
- "spec/db/migrate/20110502223022_create_monocle_views.rb",
54
- "spec/spec_helper.rb",
55
- "spec/view_spec.rb",
56
- "spec/viewable_spec.rb"
57
- ]
58
-
59
- if s.respond_to? :specification_version then
60
- s.specification_version = 3
61
-
62
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
63
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
64
- s.add_development_dependency(%q<database_cleaner>, [">= 0"])
65
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
66
- s.add_development_dependency(%q<mocha>, [">= 0"])
67
- s.add_development_dependency(%q<rails>, [">= 0"])
68
- s.add_development_dependency(%q<rcov>, [">= 0"])
69
- s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
70
- s.add_development_dependency(%q<sinatra>, [">= 0"])
71
- s.add_development_dependency(%q<sqlite3>, [">= 0"])
72
- s.add_development_dependency(%q<shoulda-matchers>, ["= 1.0.0.beta1"])
73
- s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
74
- else
75
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
76
- s.add_dependency(%q<database_cleaner>, [">= 0"])
77
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
78
- s.add_dependency(%q<mocha>, [">= 0"])
79
- s.add_dependency(%q<rails>, [">= 0"])
80
- s.add_dependency(%q<rcov>, [">= 0"])
81
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
82
- s.add_dependency(%q<sinatra>, [">= 0"])
83
- s.add_dependency(%q<sqlite3>, [">= 0"])
84
- s.add_dependency(%q<shoulda-matchers>, ["= 1.0.0.beta1"])
85
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
86
- end
87
- else
88
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
89
- s.add_dependency(%q<database_cleaner>, [">= 0"])
90
- s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
91
- s.add_dependency(%q<mocha>, [">= 0"])
92
- s.add_dependency(%q<rails>, [">= 0"])
93
- s.add_dependency(%q<rcov>, [">= 0"])
94
- s.add_dependency(%q<rspec>, ["~> 2.3.0"])
95
- s.add_dependency(%q<sinatra>, [">= 0"])
96
- s.add_dependency(%q<sqlite3>, [">= 0"])
97
- s.add_dependency(%q<shoulda-matchers>, ["= 1.0.0.beta1"])
98
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
99
- end
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.add_dependency('activesupport', '>= 2.3.10')
21
+ s.add_dependency('redis', '>= 2.2.2')
22
+ s.add_dependency('sinatra', '>= 0')
100
23
  end
101
-
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+
3
+ class TestObject
4
+ include Monocle
5
+
6
+ monocle_options cache_view_counts: true
7
+
8
+ monocle_views overall: -> { 'overall' },
9
+ yearly: -> { Time.now.beginning_of_year },
10
+ monthly: -> { Time.now.beginning_of_month },
11
+ weekly: -> { Time.now.beginning_of_week },
12
+ daily: -> { Time.now.beginning_of_day },
13
+ hourly: -> { Time.now.beginning_of_hour },
14
+ quarterly: -> { Time.now.beginning_of_quarter }
15
+
16
+ attr_accessor :id
17
+ attr_accessor :overall_views, :yearly_views, :monthly_views
18
+ attr_accessor :weekly_views, :daily_views, :hourly_views
19
+ attr_accessor :quarterly_views
20
+
21
+ def self.find(id)
22
+ o = new
23
+ o.id = id.to_i
24
+ o
25
+ end
26
+
27
+ def initialize
28
+ @id = '12345'
29
+ @overall_views = 0
30
+ end
31
+
32
+ def update_column(field, count)
33
+ self.send("#{field}=", count)
34
+ end
35
+ end
36
+
37
+ describe Monocle do
38
+ let(:object) { TestObject.new }
39
+
40
+ describe '#most_viewed_since' do
41
+ before do
42
+ 10.times do |i|
43
+ o = TestObject.new
44
+ o.id = i
45
+ (i + 10).times do
46
+ o.view!
47
+ end
48
+ end
49
+ end
50
+
51
+ it 'returns top viewed objects since a given time' do
52
+ viewed = TestObject.most_viewed_since(Time.now.beginning_of_day)
53
+ viewed.class.should == Array
54
+ viewed.first.id.should == TestObject.find(9).id
55
+ end
56
+ end
57
+
58
+ describe '#destroy_views' do
59
+ it 'deletes views for object' do
60
+ object.view!
61
+ object.destroy_views
62
+ REDIS.hget('monocle:test_object:12345', 'overall_views').should == nil
63
+ end
64
+ end
65
+
66
+ describe '#view!' do
67
+ before do
68
+ 50.times { object.view! }
69
+ end
70
+
71
+ after do
72
+ object.destroy_views
73
+ end
74
+
75
+ %w(overall yearly monthly weekly daily hourly quarterly).each do |view_type|
76
+ it "sets #{view_type} views count" do
77
+ object.send("#{view_type}_views_count").should == 50
78
+ end
79
+
80
+ it "updates cached #{view_type} views count" do
81
+ object.send("#{view_type}_views").should == 50
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,44 +1,21 @@
1
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
- require "rspec"
4
- require "monocle"
5
3
 
6
- require "rails"
7
- require "active_record"
8
- require "database_cleaner"
9
- require "shoulda-matchers"
10
-
11
- ActiveRecord::Migration.verbose = false
12
- ActiveRecord::Base.logger = Logger.new(nil)
13
-
14
- ActiveRecord::Base.establish_connection(
15
- :adapter => "sqlite3",
16
- :database => File.expand_path("spec/db/monocle.sqlite3"))
17
-
18
- ActiveRecord::Migrator.migrate(File.expand_path("spec/db/migrate"))
4
+ require 'rspec'
5
+ require 'monocle'
6
+ require 'rails'
7
+ require 'redis'
19
8
 
20
9
  # Requires supporting files with custom matchers and macros, etc,
21
10
  # in ./support/ and its subdirectories.
22
11
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
23
12
 
24
- def create_view(options = {})
25
- values = {
26
- :viewable_type => "Viewable",
27
- :viewable_id => 1,
28
- :viewed_on_start_date => Time.now - 10.days,
29
- :views => 10 }.merge(options)
30
-
31
- %w( Daily Weekly Monthly Yearly Overall ).each do |time_span|
32
- klass = "Monocle::#{time_span}View".constantize
33
- klass.create(values)
34
- end
35
- end
13
+ REDIS = Redis.new
36
14
 
37
15
  RSpec.configure do |config|
38
- config.mock_with :mocha
39
-
40
16
  config.after(:each) do
41
- DatabaseCleaner.strategy = :truncation
42
- DatabaseCleaner.clean
17
+ REDIS.keys('monocle*').each do |key|
18
+ REDIS.del(key)
19
+ end
43
20
  end
44
21
  end
metadata CHANGED
@@ -1,208 +1,114 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: monocle
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
4
5
  prerelease:
5
- version: 0.1.1
6
6
  platform: ruby
7
- authors:
8
- - Russ Smith
7
+ authors:
8
+ - Russ Smith (russ@bashme.org)
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-08-31 00:00:00 -07:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: bundler
18
- requirement: &id001 !ruby/object:Gem::Requirement
12
+ date: 2011-08-31 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
21
- - - ~>
22
- - !ruby/object:Gem::Version
23
- version: 1.0.0
24
- type: :development
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.3.10
22
+ type: :runtime
25
23
  prerelease: false
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: database_cleaner
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
30
25
  none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
35
- type: :development
36
- prerelease: false
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: jeweler
40
- requirement: &id003 !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- version: 1.5.2
46
- type: :development
47
- prerelease: false
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
50
- name: mocha
51
- requirement: &id004 !ruby/object:Gem::Requirement
52
- none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: "0"
57
- type: :development
58
- prerelease: false
59
- version_requirements: *id004
60
- - !ruby/object:Gem::Dependency
61
- name: rails
62
- requirement: &id005 !ruby/object:Gem::Requirement
63
- none: false
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: "0"
68
- type: :development
69
- prerelease: false
70
- version_requirements: *id005
71
- - !ruby/object:Gem::Dependency
72
- name: rcov
73
- requirement: &id006 !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.3.10
30
+ - !ruby/object:Gem::Dependency
31
+ name: redis
32
+ requirement: !ruby/object:Gem::Requirement
74
33
  none: false
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: "0"
79
- type: :development
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 2.2.2
38
+ type: :runtime
80
39
  prerelease: false
81
- version_requirements: *id006
82
- - !ruby/object:Gem::Dependency
83
- name: rspec
84
- requirement: &id007 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
85
41
  none: false
86
- requirements:
87
- - - ~>
88
- - !ruby/object:Gem::Version
89
- version: 2.3.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: *id007
93
- - !ruby/object:Gem::Dependency
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.2.2
46
+ - !ruby/object:Gem::Dependency
94
47
  name: sinatra
95
- requirement: &id008 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
96
49
  none: false
97
- requirements:
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- version: "0"
101
- type: :development
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
102
55
  prerelease: false
103
- version_requirements: *id008
104
- - !ruby/object:Gem::Dependency
105
- name: sqlite3
106
- requirement: &id009 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
107
57
  none: false
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- version: "0"
112
- type: :development
113
- prerelease: false
114
- version_requirements: *id009
115
- - !ruby/object:Gem::Dependency
116
- name: shoulda-matchers
117
- requirement: &id010 !ruby/object:Gem::Requirement
118
- none: false
119
- requirements:
120
- - - "="
121
- - !ruby/object:Gem::Version
122
- version: 1.0.0.beta1
123
- type: :development
124
- prerelease: false
125
- version_requirements: *id010
126
- - !ruby/object:Gem::Dependency
127
- name: yard
128
- requirement: &id011 !ruby/object:Gem::Requirement
129
- none: false
130
- requirements:
131
- - - ~>
132
- - !ruby/object:Gem::Version
133
- version: 0.6.0
134
- type: :development
135
- prerelease: false
136
- version_requirements: *id011
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
137
62
  description: A history of view events.
138
63
  email: russ@bashme.org
139
64
  executables: []
140
-
141
65
  extensions: []
142
-
143
- extra_rdoc_files:
144
- - LICENSE.txt
145
- - README.rdoc
146
- files:
66
+ extra_rdoc_files: []
67
+ files:
147
68
  - .document
69
+ - .gitignore
148
70
  - .rspec
71
+ - .travis.yml
149
72
  - Gemfile
73
+ - Guardfile
150
74
  - LICENSE.txt
151
75
  - README.rdoc
152
76
  - Rakefile
153
77
  - VERSION
154
- - lib/generators/monocle/install_generator.rb
155
- - lib/generators/monocle/templates/migration.rb
156
78
  - lib/monocle.rb
157
- - lib/monocle/daily_view.rb
158
- - lib/monocle/monthly_view.rb
159
- - lib/monocle/overall_view.rb
79
+ - lib/monocle/core_ext.rb
160
80
  - lib/monocle/server.rb
161
- - lib/monocle/view.rb
162
- - lib/monocle/views.rb
163
- - lib/monocle/weekly_view.rb
164
- - lib/monocle/yearly_view.rb
81
+ - lib/monocle/version.rb
165
82
  - monocle.gemspec
166
- - spec/db/migrate/20110502201938_create_viewables.rb
167
- - spec/db/migrate/20110502223022_create_monocle_views.rb
83
+ - spec/monocle_spec.rb
168
84
  - spec/spec_helper.rb
169
- - spec/view_spec.rb
170
- - spec/viewable_spec.rb
171
85
  - transparent.gif
172
- has_rdoc: true
173
86
  homepage: http://github.com/russ/monocle
174
- licenses:
87
+ licenses:
175
88
  - MIT
176
89
  post_install_message:
177
90
  rdoc_options: []
178
-
179
- require_paths:
91
+ require_paths:
180
92
  - lib
181
- required_ruby_version: !ruby/object:Gem::Requirement
93
+ required_ruby_version: !ruby/object:Gem::Requirement
182
94
  none: false
183
- requirements:
184
- - - ">="
185
- - !ruby/object:Gem::Version
186
- hash: -1431136823852847594
187
- segments:
188
- - 0
189
- version: "0"
190
- required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
100
  none: false
192
- requirements:
193
- - - ">="
194
- - !ruby/object:Gem::Version
195
- version: "0"
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
196
105
  requirements: []
197
-
198
106
  rubyforge_project:
199
- rubygems_version: 1.6.2
107
+ rubygems_version: 1.8.23
200
108
  signing_key:
201
109
  specification_version: 3
202
110
  summary: A history of view events.
203
- test_files:
204
- - spec/db/migrate/20110502201938_create_viewables.rb
205
- - spec/db/migrate/20110502223022_create_monocle_views.rb
111
+ test_files:
112
+ - spec/monocle_spec.rb
206
113
  - spec/spec_helper.rb
207
- - spec/view_spec.rb
208
- - spec/viewable_spec.rb
114
+ has_rdoc:
@@ -1,17 +0,0 @@
1
- require "rails/generators/active_record"
2
-
3
- module Monocle
4
- class InstallGenerator < Rails::Generators::Base
5
- include Rails::Generators::Migration
6
-
7
- source_root File.expand_path("../templates", __FILE__)
8
-
9
- def copy_migration
10
- migration_template "migration.rb", "db/migrate/create_monocle_views.rb"
11
- end
12
-
13
- def self.next_migration_number(dirname)
14
- ActiveRecord::Generators::Base.next_migration_number(dirname)
15
- end
16
- end
17
- end
@@ -1,21 +0,0 @@
1
- class CreateMonocleViews < ActiveRecord::Migration
2
- def self.up
3
- create_table :views do |t|
4
- t.string :viewable_type
5
- t.integer :viewable_id
6
- t.string :type
7
- t.date :viewed_on_start_date
8
- t.integer :views, :default => 0
9
- t.timestamps
10
- end
11
-
12
- add_index :views, [ :type, :viewable_type, :viewable_id, :views ], :name => :viewable_type_views
13
- add_index :views, [ :type, :viewable_type, :viewed_on_start_date, :viewable_id, :views ], :name => :viewable_type_start_date_views
14
- add_index :views, [ :type, :viewable_type, :viewed_on_start_date, :viewable_id ], :name => :unique_time_period_views, :unique => true
15
- add_index :views, [ :viewable_type, :viewable_id], :name => :index_views_on_viewable_type_and_viewable_id
16
- end
17
-
18
- def self.down
19
- drop_table :views
20
- end
21
- end
@@ -1,22 +0,0 @@
1
- module Monocle
2
- class DailyView < View
3
- validates_presence_of :viewed_on_start_date
4
-
5
- def view!
6
- %w( Weekly Monthly Yearly Overall ).each do |time_span|
7
- klass = "Monocle::#{time_span}View".constantize
8
- start_date = nil
9
-
10
- unless time_span == "Overall"
11
- time_span.downcase!.gsub!("ly", "")
12
- start_date = viewed_on_start_date.send("beginning_of_#{time_span}")
13
- end
14
-
15
- view = klass.find_or_create_by_viewable_type_and_viewable_id_and_viewed_on_start_date(viewable_type, viewable_id, start_date)
16
- view.view!
17
- end
18
-
19
- super
20
- end
21
- end
22
- end
@@ -1,5 +0,0 @@
1
- module Monocle
2
- class MonthlyView < View
3
- validates_presence_of :viewed_on_start_date
4
- end
5
- end
@@ -1,4 +0,0 @@
1
- module Monocle
2
- class OverallView < View
3
- end
4
- end
@@ -1,38 +0,0 @@
1
- module Monocle
2
- class View < ActiveRecord::Base
3
- belongs_to :viewable, :polymorphic => true
4
-
5
- validates_presence_of :viewable_id
6
- validates_presence_of :viewable_type
7
-
8
- def self.by_type(type)
9
- where(:type => type)
10
- end
11
-
12
- def self.since(since, options = {})
13
- options[:limit] ||= 10
14
- options[:order] ||= "SUM(views) DESC"
15
- options[:type] ||= :yearly
16
-
17
- by_type("Monocle::" + options[:type].to_s.classify + "View")
18
- .where("viewed_on_start_date >= ?", Date.today - since)
19
- .group("viewable_type, viewable_id")
20
- .order(options[:order])
21
- .limit(options[:limit])
22
- end
23
-
24
- def self.all_time(options = {})
25
- options[:limit] ||= 10
26
- options[:order] ||= "views DESC"
27
-
28
- by_type("Monocle::OverallView")
29
- .group("viewable_type, viewable_id")
30
- .order(options[:order])
31
- .limit(options[:limit])
32
- end
33
-
34
- def view!
35
- increment!(:views)
36
- end
37
- end
38
- end
@@ -1,26 +0,0 @@
1
- module Monocle
2
- module Views
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- has_one :overall_view, :as => :viewable, :dependent => :destroy, :class_name => "Monocle::OverallView"
7
-
8
- %w( daily weekly monthly yearly ).each do |time_span|
9
- has_many "#{time_span}_views".to_sym, :as => :viewable, :dependent => :destroy, :class_name => "Monocle::#{time_span.classify}View"
10
- end
11
- end
12
-
13
- module ClassMethods
14
- def viewed_since(since, options = {})
15
- View.since(since, { :viewable_type => self }.merge(options))
16
- end
17
- end
18
-
19
- delegate :views, :to => :overall_view, :prefix => :overall, :allow_nil => true
20
-
21
- def view!
22
- view = DailyView.find_or_create_by_viewable_type_and_viewable_id_and_viewed_on_start_date(self.class.name, id, Date.today)
23
- view.view!
24
- end
25
- end
26
- end
@@ -1,5 +0,0 @@
1
- module Monocle
2
- class WeeklyView < View
3
- validates_presence_of :viewed_on_start_date
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- module Monocle
2
- class YearlyView < View
3
- validates_presence_of :viewed_on_start_date
4
- end
5
- end
@@ -1,11 +0,0 @@
1
- class CreateViewables < ActiveRecord::Migration
2
- def self.up
3
- create_table :viewables do |t|
4
- t.timestamps
5
- end
6
- end
7
-
8
- def self.down
9
- drop_table :viewables
10
- end
11
- end
@@ -1,21 +0,0 @@
1
- class CreateMonocleViews < ActiveRecord::Migration
2
- def self.up
3
- create_table :views do |t|
4
- t.string :viewable_type
5
- t.integer :viewable_id
6
- t.string :type
7
- t.date :viewed_on_start_date
8
- t.integer :views, :default => 0
9
- t.timestamps
10
- end
11
-
12
- add_index :views, [ :type, :viewable_type, :viewable_id, :views ], :name => :viewable_type_views
13
- add_index :views, [ :type, :viewable_type, :viewed_on_start_date, :viewable_id, :views ], :name => :viewable_type_start_date_views
14
- add_index :views, [ :type, :viewable_type, :viewed_on_start_date, :viewable_id ], :name => :unique_time_period_views, :unique => true
15
- add_index :views, [ :viewable_type, :viewable_id], :name => :index_views_on_viewable_type_and_viewable_id
16
- end
17
-
18
- def self.down
19
- drop_table :views
20
- end
21
- end
@@ -1,36 +0,0 @@
1
- require "spec_helper"
2
-
3
- include Monocle
4
-
5
- describe View do
6
- describe "since" do
7
- before do
8
- 50.times do |i|
9
- create_view(
10
- :viewable_id => i,
11
- :viewed_on_start_date => Time.now - i.days,
12
- :views => rand(100))
13
- end
14
- end
15
-
16
- # TODO: Find a decent way to test this.
17
- it "returns most viewed objects since the given date" do
18
- View.since(7.days).all.count.should == 8
19
- end
20
- end
21
-
22
- describe "all_time" do
23
- before do
24
- 50.times do |i|
25
- create_view(
26
- :viewable_id => i,
27
- :viewed_on_start_date => Time.now - i.days,
28
- :views => rand(100))
29
- end
30
- end
31
-
32
- it "returns most viewed objects of all time" do
33
- View.all_time.all.count.should == 10
34
- end
35
- end
36
- end
@@ -1,36 +0,0 @@
1
- require "spec_helper"
2
-
3
- class Viewable < ActiveRecord::Base
4
- include Monocle::Views
5
- end
6
-
7
- describe Viewable do
8
- subject { Viewable.new }
9
-
10
- it { should have_one(:overall_view) }
11
- it { should have_many(:daily_views) }
12
- it { should have_many(:weekly_views) }
13
- it { should have_many(:monthly_views) }
14
- it { should have_many(:yearly_views) }
15
-
16
- describe "view!" do
17
- before do
18
- subject.save!
19
- end
20
-
21
- it "should increment overall view count" do
22
- subject.view!
23
- subject.overall_views.should == 1
24
- end
25
-
26
- %w( daily weekly monthly yearly ).each do |time_span|
27
- it "should increment #{time_span} view count" do
28
- 5.times do |i|
29
- view = "#{time_span}_views"
30
- subject.view!
31
- subject.send(view).first.views.should == i + 1
32
- end
33
- end
34
- end
35
- end
36
- end