monocle 0.1.1 → 0.2.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,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