punching_bag 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,104 @@
1
+ class Punch < ActiveRecord::Base
2
+
3
+ belongs_to :punchable, :polymorphic => true
4
+
5
+ attr_accessible # none
6
+
7
+ before_validation :set_defaults
8
+ validates :punchable_id, :punchable_type, :starts_at, :ends_at, :average_time, :hits, :presence => true
9
+
10
+ default_scope order('punches.average_time DESC')
11
+ scope :combos, where('punches.hits > 1')
12
+ scope :jabs, where(:hits => 1)
13
+ scope :before, lambda{ |time| where('punches.ends_at <= ?', time) }
14
+ scope :after, lambda{ |*args|
15
+ time = args.first
16
+ time.nil?? scoped : where('punches.average_time >= ?', time)
17
+ }
18
+ scope :by_timeframe, lambda{ |timeframe, time|
19
+ where('punches.starts_at >= ? AND punches.ends_at <= ?', time.send("beginning_of_#{timeframe}"), time.send("end_of_#{timeframe}"))
20
+ }
21
+ scope :by_day, lambda { |day| by_timeframe(:day, day) }
22
+ scope :by_month, lambda { |month| by_timeframe(:month, month) }
23
+ scope :by_year, lambda { |year|
24
+ year = DateTime.new(year) if year.is_a? Integer
25
+ by_timeframe(:year, year)
26
+ }
27
+
28
+ def jab?
29
+ hits == 1
30
+ end
31
+
32
+ def combo?
33
+ hits > 1
34
+ end
35
+
36
+ def timeframe
37
+ if starts_at.month != ends_at.month
38
+ :year
39
+ elsif starts_at.day != ends_at.day
40
+ :month
41
+ elsif starts_at != ends_at
42
+ :day
43
+ else
44
+ :second
45
+ end
46
+ end
47
+
48
+ def day_combo?
49
+ timeframe == :day
50
+ end
51
+
52
+ def month_combo?
53
+ timeframe == :month
54
+ end
55
+
56
+ def year_combo?
57
+ timeframe == :year
58
+ end
59
+
60
+ def find_combo_for(timeframe)
61
+ punches = punchable.punches.by_timeframe(timeframe, average_time)
62
+ punches.combos.first || punches.first
63
+ end
64
+
65
+ def combine_with(combo)
66
+ if combo != self
67
+ combo.starts_at = starts_at if starts_at < combo.starts_at
68
+ combo.ends_at = ends_at if ends_at > combo.ends_at
69
+ combo.average_time = PunchingBag.average_time(combo, self)
70
+ combo.hits += hits
71
+ self.destroy if combo.save
72
+ end
73
+ combo
74
+ end
75
+
76
+ def combine_by_day
77
+ unless day_combo? || month_combo? || year_combo?
78
+ combine_with find_combo_for(:day)
79
+ end
80
+ end
81
+
82
+ def combine_by_month
83
+ unless month_combo? || year_combo?
84
+ combine_with find_combo_for(:month)
85
+ end
86
+ end
87
+
88
+ def combine_by_year
89
+ unless year_combo?
90
+ combine_with find_combo_for(:year)
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def set_defaults
97
+ if date = (self.starts_at ||= DateTime.now)
98
+ self.ends_at ||= date
99
+ self.average_time ||= date
100
+ self.hits ||= 1
101
+ end
102
+ end
103
+
104
+ end
@@ -0,0 +1,20 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class PunchingBagGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+ source_root File.join(File.dirname(__FILE__), 'templates')
7
+
8
+ def self.next_migration_number(dirname)
9
+ sleep 1
10
+ if ActiveRecord::Base.timestamped_migrations
11
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
12
+ else
13
+ "%.3d" % (current_migration_number(dirname) + 1)
14
+ end
15
+ end
16
+
17
+ def create_migration_file
18
+ migration_template 'create_punches_table.rb', 'db/migrate/create_punches_table.rb'
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class CreatePunchesTable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :punches do |t|
4
+ t.integer :punchable_id, :null => false
5
+ t.string :punchable_type, :null => false, :limit => 20
6
+ t.datetime :starts_at, :null => false
7
+ t.datetime :ends_at, :null => false
8
+ t.datetime :average_time, :null => false
9
+ t.integer :hits, :null => false, :default=>1
10
+ end
11
+ add_index :punches, [:punchable_type, :punchable_id], :name => :punchable_index, :unique => false
12
+ add_index :punches, :average_time, :unique => false
13
+ end
14
+
15
+ def self.down
16
+ remove_index :punches, :name => :punchable_index
17
+ remove_index :punches, :average_time
18
+ drop_table :punches
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ module PunchingBag
2
+ require 'punching_bag/engine' if defined?(Rails)
3
+ require 'punching_bag/acts_as_punchable'
4
+
5
+ def self.punch(punchable, request=nil)
6
+ if request.try(:bot?)
7
+ false
8
+ else
9
+ p = Punch.new
10
+ p.punchable = punchable
11
+ p.save ? p : false
12
+ end
13
+ end
14
+
15
+ def self.average_time(*punches)
16
+ total_time = 0
17
+ hits = 0
18
+ punches.each do |punch|
19
+ total_time += punch.average_time.to_f * punch.hits
20
+ hits += punch.hits
21
+ end
22
+ Time.zone.at(total_time / hits)
23
+ end
24
+
25
+ end
@@ -0,0 +1,31 @@
1
+ module PunchingBag
2
+ module ActiveRecord
3
+
4
+ module ClassMethods
5
+ def most_hit(since=nil, limit=5)
6
+ query = self.scoped.joins(:punches).group(:punchable_type, :punchable_id)
7
+ query = query.where('punches.average_time >= ?', since) unless since.nil?
8
+ query.order('SUM(punches.hits) DESC').limit(limit)
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ def hits(since=nil)
14
+ self.punches.after(since).sum(:hits)
15
+ end
16
+
17
+ def punch(request=nil)
18
+ PunchingBag.punch(self, request)
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ class ActiveRecord::Base
26
+ def self.acts_as_punchable
27
+ extend PunchingBag::ActiveRecord::ClassMethods
28
+ include PunchingBag::ActiveRecord::InstanceMethods
29
+ has_many :punches, :as => :punchable
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ # Adds methods to enable tracking tags through a common polymorphic association
2
+ module ActsAsTaggableOn
3
+ class Tag
4
+ def self.most_hit(since=nil, limit=5)
5
+ query = Tagging.scoped.
6
+ joins('INNER JOIN punches ON (taggings.taggable_id = punches.punchable_id AND taggings.taggable_type = punches.punchable_type)').
7
+ group(:tag_id).
8
+ order('SUM(punches.hits) DESC').
9
+ limit(limit)
10
+ query = query.where('punches.average_time >= ?', since) if since
11
+ query.map(&:tag)
12
+ end
13
+
14
+ def hits(since=nil)
15
+ query = Tagging.scoped.
16
+ joins('INNER JOIN punches ON (taggings.taggable_id = punches.punchable_id AND taggings.taggable_type = punches.punchable_type)').
17
+ where(:tag_id => self.id)
18
+ query = query.where('punches.average_time >= ?', since) if since
19
+ query.sum('punches.hits')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ require 'punching_bag'
2
+ require 'rails'
3
+ require 'active_record'
4
+
5
+ module PunchingBag
6
+ class Engine < Rails::Engine
7
+ initializer 'punching_bag.extend_acts_as_taggable_on' do
8
+ require 'punching_bag/acts_as_taggable_on' if defined? ActsAsTaggableOn
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module PunchingBag
2
+
3
+ VERSION = '0.1.0'
4
+
5
+ end
@@ -0,0 +1,29 @@
1
+ namespace :punching_bag do
2
+
3
+ desc 'Combine old hit records together to improve performance'
4
+ task :combine, :by_day_after, :by_month_after, :by_year_after, :needs=>:environment do |t, args|
5
+ args.with_defaults :by_day_after => 7, :by_month_after => 1, :by_year_after => 1
6
+
7
+ punchables = Punch.all.map(&:punchable).uniq
8
+ punchables.each do |punchable|
9
+
10
+ # by_year
11
+ punchable.punches.before(args[:by_year_after].years.ago).each do |punch|
12
+ punch.combine_by_year
13
+ end
14
+
15
+ # by_month
16
+ punchable.punches.before(args[:by_month_after].months.ago).each do |punch|
17
+ punch.combine_by_month
18
+ end
19
+
20
+ # by_day
21
+ punchable.punches.before(args[:by_day_after].days.ago).each do |punch|
22
+ punch.combine_by_day
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: punching_bag
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Adam Crownoble
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-23 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: voight_kampff
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 25
29
+ segments:
30
+ - 0
31
+ - 1
32
+ - 1
33
+ version: 0.1.1
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ description: PunchingBag is a hit counting and simple trending engine for Ruby on Rails
37
+ email: adam@obledesign.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - app/models/punch.rb
46
+ - lib/punching_bag/acts_as_punchable.rb
47
+ - lib/punching_bag/version.rb
48
+ - lib/punching_bag/engine.rb
49
+ - lib/punching_bag/acts_as_taggable_on.rb
50
+ - lib/generators/punching_bag/punching_bag_generator.rb
51
+ - lib/generators/punching_bag/templates/create_punches_table.rb
52
+ - lib/punching_bag.rb
53
+ - lib/tasks/punching_bag.rake
54
+ homepage: https://github.com/adamcrown/punching_bag
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options: []
59
+
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.8.2
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: PunchingBag hit conter and trending plugin
87
+ test_files: []
88
+