pivotable 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown ADDED
@@ -0,0 +1,3 @@
1
+ # Pivotable
2
+
3
+ TODO
@@ -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
+