active_record_calculator 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in active_record_calculator.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "active_record_calculator/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "active_record_calculator"
7
+ s.version = ActiveRecordCalculator::VERSION
8
+ s.authors = ["Grady Griffin"]
9
+ s.email = ["gradyg@izea.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{ActiveRecord Calculations done faster}
12
+ s.description = %q{active_record_calculator does groupable aggregate functions in one sql call for better performance}
13
+
14
+ s.rubyforge_project = "active_record_calculator"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ # s.add_runtime_dependency "rest-client"
24
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
25
+ s.add_runtime_dependency('activerecord')
26
+ s.add_development_dependency("rspec")
27
+ else
28
+ s.add_dependency('activerecord')
29
+ s.add_development_dependency("rspec")
30
+ end
31
+ end
@@ -0,0 +1,71 @@
1
+ module ActiveRecordCalculator
2
+ class CalculatorProxy
3
+ def initialize(klass, finder_options = {})
4
+ @klass = klass
5
+ @operations = []
6
+ @columns = []
7
+ @finder_options = finder_options.except(:order, :select)
8
+ end
9
+
10
+ def col(column_name, as = nil)
11
+ add_column(column_name, as)
12
+ end
13
+ alias :column :col
14
+
15
+ def cnt(column_name, as, options = {})
16
+ add_operation(:count, column_name, as, options)
17
+ end
18
+ alias :count :cnt
19
+
20
+ def sum(column_name, as, conditions = {})
21
+ add_operation(:sum, column_name, as, options)
22
+ end
23
+
24
+ def avg(column_name, as, conditions = {})
25
+ add_operation(:avg, column_name, as, options)
26
+ end
27
+ alias :average :avg
28
+
29
+ def max(column_name, as, conditions = {})
30
+ add_operation(:max, column_name, as, options)
31
+ end
32
+ alias :maximum :max
33
+
34
+ def min(column_name, as, conditions = {})
35
+ add_operation(:min, column_name, as, options)
36
+ end
37
+ alias :minimum :min
38
+
39
+ def calculate
40
+ sql = @klass.send(:construct_finder_sql, @finder_options)
41
+ sql.gsub!(/^SELECT \*/, select)
42
+ @operations = []
43
+ @klass.find_by_sql(sql)
44
+ end
45
+
46
+ def set_finder_options(finder_options = {})
47
+ @finder_options = finder_options.except(:select)
48
+ end
49
+
50
+ def select
51
+ s = ["SELECT\n"]
52
+ s += @columns.join(', ') + "\n"
53
+ s += @operations.collect {|op| op.build_select(@klass)}.join(",\n")
54
+ end
55
+
56
+ private
57
+
58
+ def add_column(column_name, as)
59
+ if as
60
+ @columns << column_name
61
+ else
62
+ @columns << "#{column_name} AS #{as}"
63
+ end
64
+ end
65
+
66
+ def add_operation(op, column_name, as, options)
67
+ options = {:conditions => options} if options.is_a?(String)
68
+ @operations << Operation.new(op, column_name, as, options)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,36 @@
1
+ module ActiveRecordCalculator
2
+ class Operation
3
+
4
+ def initialize(op, column, as, options)
5
+ @op = op
6
+ @column = column
7
+ @as = as
8
+ @options = options
9
+ end
10
+
11
+ def build_select(klass)
12
+ mock = klass.send(:construct_calculation_sql, @op, @column, @options)
13
+ all, sel, cond = if mock =~ /\s+WHERE\s+/
14
+ mock.match(/^SELECT\s+(.*)\s+AS.*\s+WHERE\s?(.*)/).to_a
15
+ else
16
+ mock.match(/^SELECT\s+(.*)\s+AS/).to_a
17
+ end
18
+ return "#{sel} AS #{@as}" unless cond
19
+ i = sel.index('(')
20
+ start = sel[0..i]
21
+ mid = "CASE #{cond} WHEN 1 THEN #{sel[i+1..-2]} ELSE #{default} END"
22
+ fin = ") AS #{@as}"
23
+ "#{start}#{mid}#{fin}"
24
+ end
25
+
26
+ def inspect
27
+ "#{@as} => #{@op}"
28
+ end
29
+
30
+ private
31
+
32
+ def default
33
+ @op == :sum ? 0 : 'NULL'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveRecordCalculator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,21 @@
1
+ require "active_record_calculator/version"
2
+
3
+ module ActiveRecordCalculator
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def calculate_many(options = {}, &blk)
10
+ calculator = CalculatorProxy.new(self, options)
11
+ yield calculator
12
+ calculator.calculate
13
+ end
14
+
15
+ def calculator
16
+ CalculatorProxy.new(self)
17
+ end
18
+ end
19
+ end
20
+
21
+ ::ActiveRecord::Base.send :include, ActiveRecordCalculator
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_record_calculator
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Grady Griffin
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-27 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: active_record_calculator does groupable aggregate functions in one sql call for better performance
49
+ email:
50
+ - gradyg@izea.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - Rakefile
61
+ - active_record_calculator.gemspec
62
+ - lib/active_record_calculator.rb
63
+ - lib/active_record_calculator/calculator_proxy.rb
64
+ - lib/active_record_calculator/operation.rb
65
+ - lib/active_record_calculator/version.rb
66
+ homepage: ""
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options: []
71
+
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project: active_record_calculator
95
+ rubygems_version: 1.8.15
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: ActiveRecord Calculations done faster
99
+ test_files: []
100
+