pivotable 0.0.2
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/README.markdown +3 -0
- data/lib/pivotable/collection.rb +17 -0
- data/lib/pivotable/column.rb +35 -0
- data/lib/pivotable/model.rb +55 -0
- data/lib/pivotable/pivoting.rb +19 -0
- data/lib/pivotable/rotation.rb +86 -0
- data/lib/pivotable.rb +19 -0
- metadata +122 -0
data/README.markdown
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class Pivotable::Collection < Hash
|
2
|
+
|
3
|
+
attr_reader :model
|
4
|
+
|
5
|
+
def initialize(model)
|
6
|
+
@model = model
|
7
|
+
super()
|
8
|
+
end
|
9
|
+
|
10
|
+
# Adds a new rotation to this collection
|
11
|
+
def rotation(name, &block)
|
12
|
+
name, parent = name.is_a?(Hash) ? name.to_a.first : [name, nil]
|
13
|
+
item = Pivotable::Rotation.new(self, name, parent, &block)
|
14
|
+
self[item.name] = item
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Pivotable::Column
|
2
|
+
|
3
|
+
attr_reader :attribute
|
4
|
+
|
5
|
+
def initialize(model, attribute, options = {})
|
6
|
+
@as = options[:as]
|
7
|
+
@attribute = case attribute
|
8
|
+
when Arel::Attribute
|
9
|
+
attribute
|
10
|
+
else
|
11
|
+
model.arel_table[attribute]
|
12
|
+
end
|
13
|
+
raise ArgumentError, "Invalid attribute #{attribute.inspect}" unless @attribute
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
@as || attribute.name
|
18
|
+
end
|
19
|
+
|
20
|
+
def calculate!(function)
|
21
|
+
@as ||= attribute.name
|
22
|
+
@function = attribute.send(function)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_select
|
26
|
+
select = (@function || attribute).clone
|
27
|
+
select.as(@as.to_s) if @as
|
28
|
+
select
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_group
|
32
|
+
attribute.clone
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Includable module, for ActiveRecord::Base
|
2
|
+
module Pivotable::Model
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :_pivotable
|
7
|
+
self._pivotable = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
# Pivotable definition for a model. Example:
|
13
|
+
#
|
14
|
+
# class Stat < ActiveRecord::Base
|
15
|
+
#
|
16
|
+
# # Add your rotations
|
17
|
+
# pivotable do
|
18
|
+
#
|
19
|
+
# rotation :overall do
|
20
|
+
# sum :visits
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # Rotations can in herit parent definitions
|
24
|
+
# rotation :by_day => :overall do
|
25
|
+
# by :day
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # Add your custom rotations, e.g. for admins
|
31
|
+
# pivotable :admin do
|
32
|
+
#
|
33
|
+
# rotation :overall do
|
34
|
+
# sum :visits
|
35
|
+
# maximum :bounce_rate
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
def pivotable(name = nil, &block)
|
43
|
+
name = name.present? ? name.to_sym : :_default
|
44
|
+
_pivotable[name] ||= Pivotable::Collection.new(self)
|
45
|
+
_pivotable[name.to_sym].instance_eval(&block) if block
|
46
|
+
_pivotable[name]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Delegator to Relation#pivot
|
50
|
+
def pivot(*args)
|
51
|
+
scoped.pivot(*args)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Pivotable::Pivoting
|
2
|
+
|
3
|
+
# Apply pivot retations. Example:
|
4
|
+
#
|
5
|
+
# Stat.pivot(:overall)
|
6
|
+
#
|
7
|
+
# # Use custom (e.g. admin) rotation
|
8
|
+
# Stat.pivot(:admin, :by_day)
|
9
|
+
#
|
10
|
+
# # Combine it with relations & scopes
|
11
|
+
# Stat.latest.order('some_column').pivot(:by_day).paginate :page => 1
|
12
|
+
#
|
13
|
+
def pivot(*args)
|
14
|
+
rotation = args.pop
|
15
|
+
scope = args.first.is_a?(Symbol) ? args.shift : nil
|
16
|
+
klass.pivotable(scope)[rotation].merge(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class Pivotable::Rotation
|
2
|
+
|
3
|
+
attr_reader :collection, :name, :parent, :block, :selects, :groups, :joins, :loaded
|
4
|
+
delegate :model, :to => :collection
|
5
|
+
alias :loaded? :loaded
|
6
|
+
|
7
|
+
def initialize(collection, name, parent = nil, &block)
|
8
|
+
@collection = collection
|
9
|
+
@name = name.to_sym
|
10
|
+
@parent = parent.to_sym if parent
|
11
|
+
@selects = []
|
12
|
+
@groups = []
|
13
|
+
@joins = []
|
14
|
+
@block = block
|
15
|
+
@loaded = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def sum(*cols)
|
19
|
+
calculate :sum, *cols
|
20
|
+
end
|
21
|
+
|
22
|
+
def minimum(*cols)
|
23
|
+
calculate :minimum, *cols
|
24
|
+
end
|
25
|
+
|
26
|
+
def maximum(*cols)
|
27
|
+
calculate :maximum, *cols
|
28
|
+
end
|
29
|
+
|
30
|
+
def average(*cols)
|
31
|
+
calculate :average, *cols
|
32
|
+
end
|
33
|
+
|
34
|
+
def by(*cols)
|
35
|
+
cols = columns(*cols)
|
36
|
+
@selects += cols
|
37
|
+
@groups += cols
|
38
|
+
end
|
39
|
+
|
40
|
+
def calculate(function, *cols)
|
41
|
+
columns(*cols).each do |col|
|
42
|
+
col.calculate!(function)
|
43
|
+
@selects << col
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def joins(*names)
|
48
|
+
@joins += names
|
49
|
+
end
|
50
|
+
|
51
|
+
def merge(relation)
|
52
|
+
load! unless loaded?
|
53
|
+
|
54
|
+
selects.each do |column|
|
55
|
+
relation = relation.select(column.to_select)
|
56
|
+
end
|
57
|
+
|
58
|
+
groups.each do |column|
|
59
|
+
relation = relation.group(column.to_group)
|
60
|
+
end
|
61
|
+
|
62
|
+
joins.each do |join|
|
63
|
+
relation = relation.joins(join)
|
64
|
+
end
|
65
|
+
|
66
|
+
relation
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def load!
|
72
|
+
return if loaded?
|
73
|
+
|
74
|
+
instance_eval &collection[parent].block if parent
|
75
|
+
instance_eval &block
|
76
|
+
@loaded = true
|
77
|
+
end
|
78
|
+
|
79
|
+
def columns(*cols)
|
80
|
+
opts = cols.extract_options!
|
81
|
+
cols.map do |col|
|
82
|
+
Pivotable::Column.new(model, col, opts)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/lib/pivotable.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "active_support/core_ext"
|
2
|
+
require "active_record"
|
3
|
+
require "active_record/relation"
|
4
|
+
|
5
|
+
module Pivotable
|
6
|
+
autoload :Model, "pivotable/model"
|
7
|
+
autoload :Pivoting, "pivotable/pivoting"
|
8
|
+
autoload :Rotation, "pivotable/rotation"
|
9
|
+
autoload :Column, "pivotable/column"
|
10
|
+
autoload :Collection, "pivotable/collection"
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveRecord::Base.class_eval do
|
14
|
+
include ::Pivotable::Model
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveRecord::Relation.class_eval do
|
18
|
+
include ::Pivotable::Pivoting
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pivotable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Dimitrij Denissenko
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-07-05 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: abstract
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: activerecord
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 7
|
44
|
+
segments:
|
45
|
+
- 3
|
46
|
+
- 0
|
47
|
+
- 0
|
48
|
+
version: 3.0.0
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: activesupport
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ~>
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 7
|
60
|
+
segments:
|
61
|
+
- 3
|
62
|
+
- 0
|
63
|
+
- 0
|
64
|
+
version: 3.0.0
|
65
|
+
type: :runtime
|
66
|
+
version_requirements: *id003
|
67
|
+
description: Great for building APIs & reports
|
68
|
+
email: dimitrij@blacksquaremedia.com
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files: []
|
74
|
+
|
75
|
+
files:
|
76
|
+
- README.markdown
|
77
|
+
- lib/pivotable/pivoting.rb
|
78
|
+
- lib/pivotable/column.rb
|
79
|
+
- lib/pivotable/collection.rb
|
80
|
+
- lib/pivotable/rotation.rb
|
81
|
+
- lib/pivotable/model.rb
|
82
|
+
- lib/pivotable.rb
|
83
|
+
has_rdoc: true
|
84
|
+
homepage: https://github.com/bsm/pivotable
|
85
|
+
licenses: []
|
86
|
+
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
hash: 57
|
98
|
+
segments:
|
99
|
+
- 1
|
100
|
+
- 8
|
101
|
+
- 7
|
102
|
+
version: 1.8.7
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 23
|
109
|
+
segments:
|
110
|
+
- 1
|
111
|
+
- 3
|
112
|
+
- 6
|
113
|
+
version: 1.3.6
|
114
|
+
requirements: []
|
115
|
+
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 1.6.2
|
118
|
+
signing_key:
|
119
|
+
specification_version: 3
|
120
|
+
summary: Build pivotable data tables with ActiveRecord
|
121
|
+
test_files: []
|
122
|
+
|