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 CHANGED
@@ -45,17 +45,14 @@ Example:
45
45
 
46
46
  You can use statement to see the sql created
47
47
 
48
- The calculator can be used for fast direct sql updates. A calculator needs to be created where all the operations have aliases
49
- that have a respective column name in the update table:
50
-
51
- calculator = Purchases.calculator(:conditions => "created_at > '2011-07-01'", :group => "user_id")
52
- calculator.count :id, "total_purchases"
53
- calculator.cnt :id, "expensive_purchases", "price > 100"
54
- calculator.sum :price, "cheap_spending", "price < 100"
55
- calculator.avg :price, "cheap_average", "price < 100"
56
- calculator.min :price, "least_purchase"
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 only works with mysql adapters currently
58
+ Update should work with all ActiveRecord supported databases except sqlite
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ # If you want to make this the default task
7
+ task :default => :spec
@@ -37,7 +37,7 @@ module ActiveRecordCalculator
37
37
  end
38
38
  alias :minimum :min
39
39
 
40
- def table_name
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.except(:update_key))
85
+ sql = @klass.send(:construct_finder_sql, @finder_options)
94
86
  sql.gsub(/^SELECT\s+\*/i, select)
95
87
  end
96
88
 
@@ -11,11 +11,11 @@ module ActiveRecordCalculator
11
11
  end
12
12
 
13
13
  def inspect
14
- @name
14
+ @name.to_s
15
15
  end
16
16
 
17
17
  def alias_name
18
- @as
18
+ @as.to_s
19
19
  end
20
20
  end
21
21
  end
@@ -11,11 +11,11 @@ module ActiveRecordCalculator
11
11
  end
12
12
 
13
13
  def inspect
14
- @column
14
+ @column.to_s
15
15
  end
16
16
 
17
17
  def name
18
- @as
18
+ @as.to_s
19
19
  end
20
20
  end
21
21
  end
@@ -28,7 +28,11 @@ module ActiveRecordCalculator
28
28
  end
29
29
 
30
30
  def name
31
- @as
31
+ @as.to_s
32
+ end
33
+
34
+ def op
35
+ @op
32
36
  end
33
37
 
34
38
  private
@@ -9,18 +9,73 @@ module ActiveRecordCalculator
9
9
  end
10
10
 
11
11
  def statement
12
- start = %{UPDATE #{table}
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
- start += calculation_columns.collect do |col|
60
+ sql += calculation_columns.collect do |col|
18
61
  "#{table}.#{col} = sub_#{subquery_table}.#{col}"
19
62
  end.join(",\n")
20
- start
63
+ sql
21
64
  end
22
65
 
23
- private
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.table_name
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 #{update_table}" unless invalid_columns.empty?
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
 
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordCalculator
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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
@@ -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'
@@ -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: 27
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.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-31 00:00:00 -05:00
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
- has_rdoc: true
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.4.2
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