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.
- data/app/models/punch.rb +104 -0
- data/lib/generators/punching_bag/punching_bag_generator.rb +20 -0
- data/lib/generators/punching_bag/templates/create_punches_table.rb +20 -0
- data/lib/punching_bag.rb +25 -0
- data/lib/punching_bag/acts_as_punchable.rb +31 -0
- data/lib/punching_bag/acts_as_taggable_on.rb +22 -0
- data/lib/punching_bag/engine.rb +11 -0
- data/lib/punching_bag/version.rb +5 -0
- data/lib/tasks/punching_bag.rake +29 -0
- metadata +88 -0
data/app/models/punch.rb
ADDED
@@ -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
|
data/lib/punching_bag.rb
ADDED
@@ -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,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
|
+
|