active_record_calculator 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +10 -13
- data/Rakefile +6 -0
- data/lib/active_record_calculator/calculator_proxy.rb +2 -10
- data/lib/active_record_calculator/column.rb +2 -2
- data/lib/active_record_calculator/group_operation.rb +2 -2
- data/lib/active_record_calculator/operation.rb +5 -1
- data/lib/active_record_calculator/updater_proxy.rb +61 -6
- data/lib/active_record_calculator/version.rb +1 -1
- data/lib/active_record_calculator.rb +9 -0
- data/spec/calculator_proxy_spec.rb +72 -0
- data/spec/column_spec.rb +34 -0
- data/spec/group_operation_spec.rb +1 -0
- data/spec/operation_spec.rb +1 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/updater_proxy_spec.rb +1 -0
- metadata +18 -9
data/README
CHANGED
@@ -45,17 +45,14 @@ Example:
|
|
45
45
|
|
46
46
|
You can use statement to see the sql created
|
47
47
|
|
48
|
-
The
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
calculator.max :price, "most_purchase"
|
58
|
-
|
59
|
-
calculator.update(:purchase_history, :user_id) #the tables are joined by the second argument and the first grouping column
|
48
|
+
The updater can be used for fast direct sql updates. An updater needs to be created where all the operations have aliases that have a respective column name in the update table. The first two arguments are the update table and the key to join. The key is joined with the first group column which is also required.
|
49
|
+
|
50
|
+
updater = Purchases.updater(:purchase_history, :user_id, :conditions => "created_at > '2011-07-01'", :group => "user_id")
|
51
|
+
updater.count :id, "total_purchases"
|
52
|
+
updater.cnt :id, "expensive_purchases", "price > 100"
|
53
|
+
updater.sum :price, "cheap_spending", "price < 100"
|
54
|
+
updater.avg :price, "cheap_average", "price < 100"
|
55
|
+
updater.min :price, "least_purchase"
|
56
|
+
updater.max :price, "most_purchase"
|
60
57
|
|
61
|
-
Update
|
58
|
+
Update should work with all ActiveRecord supported databases except sqlite
|
data/Rakefile
CHANGED
@@ -37,7 +37,7 @@ module ActiveRecordCalculator
|
|
37
37
|
end
|
38
38
|
alias :minimum :min
|
39
39
|
|
40
|
-
def
|
40
|
+
def table
|
41
41
|
@klass.table_name
|
42
42
|
end
|
43
43
|
|
@@ -75,14 +75,6 @@ module ActiveRecordCalculator
|
|
75
75
|
connection.select_all(query)
|
76
76
|
end
|
77
77
|
|
78
|
-
def update(table, foreign_key)
|
79
|
-
unless connection.adapter_name =~ /^mysql/i
|
80
|
-
raise UnsupportedAdapterError, "Updates with the database adapter is not supported."
|
81
|
-
end
|
82
|
-
updater = UpdaterProxy.new(table, foreign_key, self)
|
83
|
-
updater
|
84
|
-
end
|
85
|
-
|
86
78
|
def find(finder_options = {})
|
87
79
|
finder_options.symbolize_keys!
|
88
80
|
@finder_options = finder_options.except(:select, :include, :from, :readonly, :lock)
|
@@ -90,7 +82,7 @@ module ActiveRecordCalculator
|
|
90
82
|
|
91
83
|
def statement
|
92
84
|
add_group_operations
|
93
|
-
sql = @klass.send(:construct_finder_sql, @finder_options
|
85
|
+
sql = @klass.send(:construct_finder_sql, @finder_options)
|
94
86
|
sql.gsub(/^SELECT\s+\*/i, select)
|
95
87
|
end
|
96
88
|
|
@@ -9,18 +9,73 @@ module ActiveRecordCalculator
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def statement
|
12
|
-
|
12
|
+
case connection.adapter_name
|
13
|
+
when /mysql/i then mysql_statement
|
14
|
+
when /postgresql/i then psql_statement
|
15
|
+
else abstract_statement
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
connection.update(statement)
|
21
|
+
end
|
22
|
+
|
23
|
+
def col(column_name, as = nil)
|
24
|
+
calculator.col(column_name, as)
|
25
|
+
end
|
26
|
+
alias :column :col
|
27
|
+
|
28
|
+
def cnt(column_name, as, options = {})
|
29
|
+
calculator.count(column_name, as, options)
|
30
|
+
end
|
31
|
+
alias :count :cnt
|
32
|
+
|
33
|
+
def sum(column_name, as, options = {})
|
34
|
+
calculator.sum(column_name, as, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def avg(column_name, as, options = {})
|
38
|
+
calculator.avg(column_name, as, options)
|
39
|
+
end
|
40
|
+
alias :average :avg
|
41
|
+
|
42
|
+
def max(column_name, as, options = {})
|
43
|
+
calculator.max(column_name, as, options)
|
44
|
+
end
|
45
|
+
alias :maximum :max
|
46
|
+
|
47
|
+
def min(column_name, as, options = {})
|
48
|
+
calculator.min(column_name, as, options)
|
49
|
+
end
|
50
|
+
alias :minimum :min
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def mysql_statement
|
55
|
+
sql = %{UPDATE #{table}
|
13
56
|
INNER JOIN
|
14
57
|
(#{calculator.statement}
|
15
58
|
) AS sub_#{subquery_table} ON sub_#{subquery_table}.group_column_1 = #{table}.#{key}
|
16
59
|
SET\n}
|
17
|
-
|
60
|
+
sql += calculation_columns.collect do |col|
|
18
61
|
"#{table}.#{col} = sub_#{subquery_table}.#{col}"
|
19
62
|
end.join(",\n")
|
20
|
-
|
63
|
+
sql
|
21
64
|
end
|
22
65
|
|
23
|
-
|
66
|
+
def psql_statement
|
67
|
+
abstract_statement.gsub(/^UPDATE/, "UPDATE ONLY")
|
68
|
+
end
|
69
|
+
|
70
|
+
def abstract_statement
|
71
|
+
sql = "UPDATE #{table} SET\n"
|
72
|
+
sql += calculation_columns.collect do |col|
|
73
|
+
"#{table}.#{col} = sub_#{subquery_table}.#{col}"
|
74
|
+
end.join(",\n")
|
75
|
+
sql += "\nFROM #{table}, (#{calculator.statement}) AS sub_#{subquery_table}\n"
|
76
|
+
sql += "WHERE sub_#{subquery_table}.group_column_1 = #{table}.#{key}"
|
77
|
+
sql
|
78
|
+
end
|
24
79
|
|
25
80
|
def connection
|
26
81
|
calculator.connection
|
@@ -44,7 +99,7 @@ module ActiveRecordCalculator
|
|
44
99
|
end
|
45
100
|
|
46
101
|
def subquery_table
|
47
|
-
calculator.
|
102
|
+
calculator.table
|
48
103
|
end
|
49
104
|
|
50
105
|
def update_columns
|
@@ -68,7 +123,7 @@ module ActiveRecordCalculator
|
|
68
123
|
end
|
69
124
|
|
70
125
|
def valid_columns?
|
71
|
-
raise InvalidColumnError, "Can not resolve update column(s) #{invalid_columns.to_sentence} for table #{
|
126
|
+
raise InvalidColumnError, "Can not resolve update column(s) #{invalid_columns.to_sentence} for table #{table}" unless invalid_columns.empty?
|
72
127
|
true
|
73
128
|
end
|
74
129
|
|
@@ -19,6 +19,15 @@ module ActiveRecordCalculator
|
|
19
19
|
yield c if blk
|
20
20
|
c
|
21
21
|
end
|
22
|
+
|
23
|
+
def updater(table, key, options = {}, &blk)
|
24
|
+
if connection.adapter_name =~ /^sqlite/i
|
25
|
+
raise UnsupportedAdapterError, "Updates with the database adapter is not supported."
|
26
|
+
end
|
27
|
+
c = CalculatorProxy.new(self, options)
|
28
|
+
yield c if blk
|
29
|
+
UpdaterProxy.new(table, key, c)
|
30
|
+
end
|
22
31
|
end
|
23
32
|
end
|
24
33
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecordCalculator::CalculatorProxy, "#new" do
|
4
|
+
it "should return a ActiveRecordCalculator::CalculatorProxy instance" do
|
5
|
+
instance = ActiveRecordCalculator::CalculatorProxy.new(FakeClass)
|
6
|
+
instance.class.to_s.should eq('ActiveRecordCalculator::CalculatorProxy')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ActiveRecordCalculator::CalculatorProxy, "#col, #column" do
|
11
|
+
it "should add a ActiveRecordCalculator::Column instance to columns" do
|
12
|
+
instance = ActiveRecordCalculator::CalculatorProxy.new(FakeClass)
|
13
|
+
instance.col(:id)
|
14
|
+
instance.column(:id)
|
15
|
+
instance.columns.length.should eq(2)
|
16
|
+
instance.columns.first.class.should eq(ActiveRecordCalculator::Column)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe ActiveRecordCalculator::CalculatorProxy, "#count, #cnt" do
|
21
|
+
it "should add a ActiveRecordCalculator::Operation instance to operations" do
|
22
|
+
instance = ActiveRecordCalculator::CalculatorProxy.new(FakeClass)
|
23
|
+
instance.count(:id, 'counter')
|
24
|
+
instance.cnt(:id, 'counter2')
|
25
|
+
instance.operations.length.should eq(2)
|
26
|
+
instance.operations.first.op.should eq(:count)
|
27
|
+
instance.operations.first.class.should eq(ActiveRecordCalculator::Operation)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ActiveRecordCalculator::CalculatorProxy, "#max, #maximum" do
|
32
|
+
it "should add a ActiveRecordCalculator::Operation instance to operations" do
|
33
|
+
instance = ActiveRecordCalculator::CalculatorProxy.new(FakeClass)
|
34
|
+
instance.max(:id, 'counter')
|
35
|
+
instance.maximum(:id, 'counter2')
|
36
|
+
instance.operations.length.should eq(2)
|
37
|
+
instance.operations.first.op.should eq(:max)
|
38
|
+
instance.operations.first.class.should eq(ActiveRecordCalculator::Operation)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe ActiveRecordCalculator::CalculatorProxy, "#min, #minimum" do
|
43
|
+
it "should add a ActiveRecordCalculator::Operation instance to operations" do
|
44
|
+
instance = ActiveRecordCalculator::CalculatorProxy.new(FakeClass)
|
45
|
+
instance.min(:id, 'counter')
|
46
|
+
instance.minimum(:id, 'counter2')
|
47
|
+
instance.operations.length.should eq(2)
|
48
|
+
instance.operations.first.op.should eq(:min)
|
49
|
+
instance.operations.first.class.should eq(ActiveRecordCalculator::Operation)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe ActiveRecordCalculator::CalculatorProxy, "#avg, #average" do
|
54
|
+
it "should add a ActiveRecordCalculator::Operation instance to operations" do
|
55
|
+
instance = ActiveRecordCalculator::CalculatorProxy.new(FakeClass)
|
56
|
+
instance.avg(:id, 'counter')
|
57
|
+
instance.average(:id, 'counter2')
|
58
|
+
instance.operations.length.should eq(2)
|
59
|
+
instance.operations.first.op.should eq(:avg)
|
60
|
+
instance.operations.first.class.should eq(ActiveRecordCalculator::Operation)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe ActiveRecordCalculator::CalculatorProxy, "#sum" do
|
65
|
+
it "should add a ActiveRecordCalculator::Operation instance to operations" do
|
66
|
+
instance = ActiveRecordCalculator::CalculatorProxy.new(FakeClass)
|
67
|
+
instance.sum(:id, 'counter')
|
68
|
+
instance.operations.length.should eq(1)
|
69
|
+
instance.operations.first.op.should eq(:sum)
|
70
|
+
instance.operations.first.class.should eq(ActiveRecordCalculator::Operation)
|
71
|
+
end
|
72
|
+
end
|
data/spec/column_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecordCalculator::Column, "#new" do
|
4
|
+
it "should return a ActiveRecordCalculator::Column instance" do
|
5
|
+
instance = ActiveRecordCalculator::Column.new('a_column', nil)
|
6
|
+
instance.class.should eq(ActiveRecordCalculator::Column)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ActiveRecordCalculator::Column, "#inpsect" do
|
11
|
+
it "should return a column name" do
|
12
|
+
instance = ActiveRecordCalculator::Column.new('a_column', nil)
|
13
|
+
instance.inspect.should eq('a_column')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe ActiveRecordCalculator::Column, "#alias" do
|
18
|
+
it "should return given alias" do
|
19
|
+
instance = ActiveRecordCalculator::Column.new('a_column', 'an_alias')
|
20
|
+
instance.alias_name.should eq('an_alias')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe ActiveRecordCalculator::Column, "#build_select" do
|
25
|
+
it "should return correct name/alias" do
|
26
|
+
instance = ActiveRecordCalculator::Column.new('a_column', 'an_alias')
|
27
|
+
instance.build_select.should eq('a_column AS an_alias')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return correct name/name when no alias given" do
|
31
|
+
instance = ActiveRecordCalculator::Column.new('a_column', nil)
|
32
|
+
instance.build_select.should eq('a_column AS a_column')
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'spec_helper'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'spec_helper'
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'active_record_calculator'
|
5
|
+
|
6
|
+
class FakeConnection
|
7
|
+
end
|
8
|
+
|
9
|
+
class FakeClass
|
10
|
+
|
11
|
+
def table_name
|
12
|
+
"some_records"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
# some (optional) config here
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'spec_helper'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_calculator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Grady Griffin
|
@@ -15,8 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-01
|
19
|
-
default_executable:
|
18
|
+
date: 2012-02-01 00:00:00 Z
|
20
19
|
dependencies:
|
21
20
|
- !ruby/object:Gem::Dependency
|
22
21
|
name: activerecord
|
@@ -69,7 +68,12 @@ files:
|
|
69
68
|
- lib/active_record_calculator/operation.rb
|
70
69
|
- lib/active_record_calculator/updater_proxy.rb
|
71
70
|
- lib/active_record_calculator/version.rb
|
72
|
-
|
71
|
+
- spec/calculator_proxy_spec.rb
|
72
|
+
- spec/column_spec.rb
|
73
|
+
- spec/group_operation_spec.rb
|
74
|
+
- spec/operation_spec.rb
|
75
|
+
- spec/spec_helper.rb
|
76
|
+
- spec/updater_proxy_spec.rb
|
73
77
|
homepage: ""
|
74
78
|
licenses: []
|
75
79
|
|
@@ -99,9 +103,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
103
|
requirements: []
|
100
104
|
|
101
105
|
rubyforge_project: active_record_calculator
|
102
|
-
rubygems_version: 1.
|
106
|
+
rubygems_version: 1.8.15
|
103
107
|
signing_key:
|
104
108
|
specification_version: 3
|
105
109
|
summary: ActiveRecord Calculations done faster
|
106
|
-
test_files:
|
107
|
-
|
110
|
+
test_files:
|
111
|
+
- spec/calculator_proxy_spec.rb
|
112
|
+
- spec/column_spec.rb
|
113
|
+
- spec/group_operation_spec.rb
|
114
|
+
- spec/operation_spec.rb
|
115
|
+
- spec/spec_helper.rb
|
116
|
+
- spec/updater_proxy_spec.rb
|