punching_bag 0.1.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,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
+