composite_primary_keys 5.0.9 → 5.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/History.rdoc CHANGED
@@ -1,3 +1,11 @@
1
+ == 5.0.10 2012-11-21
2
+ * ActiveRecord 3.2.9 compatability (Tom Hughes, Chris Heald, Jack Tang)
3
+ * Add support for find_in_batches (Charlie Savage)
4
+ * Add support for touch method (Macario Ortega)
5
+ * Remove symbolized representation of primary keys to be consistent with ActiveRecord (Tiago Cardoso)
6
+ * Update destroy method to use hash of primary keys over an array. This allows the proper SQL
7
+ (using IS NULL) to be generated when one of the primary key attributes is nil (Scott Hunter).
8
+
1
9
  == 5.0.9 2012-09-29
2
10
  * Enable tests for MS SQL Servder (Enderson Maia)
3
11
  * Update ActiveRecord::Base#initialize_dup override in line with ActiveRecord::Base 3.2.5+ (Lucas Maxwell)
@@ -26,7 +26,7 @@ $:.unshift(File.dirname(__FILE__)) unless
26
26
 
27
27
  unless defined?(ActiveRecord)
28
28
  require 'rubygems'
29
- gem 'activerecord', '>= 3.2.8', '~> 3.2.0'
29
+ gem 'activerecord', '>= 3.2.9', '~> 3.2.0'
30
30
  require 'active_record'
31
31
  end
32
32
 
@@ -56,6 +56,7 @@ require 'active_record/attribute_methods/write'
56
56
 
57
57
  require 'active_record/connection_adapters/abstract_adapter'
58
58
 
59
+ require 'active_record/relation/batches'
59
60
  require 'active_record/relation/calculations'
60
61
  require 'active_record/relation/finder_methods'
61
62
  require 'active_record/relation/query_methods'
@@ -94,6 +95,7 @@ require 'composite_primary_keys/attribute_methods/write'
94
95
  require 'composite_primary_keys/connection_adapters/abstract_adapter'
95
96
  require 'composite_primary_keys/connection_adapters/abstract/connection_specification_changes'
96
97
 
98
+ require 'composite_primary_keys/relation/batches'
97
99
  require 'composite_primary_keys/relation/calculations'
98
100
  require 'composite_primary_keys/relation/finder_methods'
99
101
  require 'composite_primary_keys/relation/query_methods'
@@ -13,12 +13,12 @@ module ActiveRecord
13
13
  # The attribute already has an unsaved change.
14
14
  if attribute_changed?(attr)
15
15
  old = @changed_attributes[attr]
16
- @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
16
+ @changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
17
17
  else
18
18
  old = clone_attribute_value(:read_attribute, attr)
19
19
  # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
20
20
  old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
21
- @changed_attributes[attr] = old if field_changed?(attr, old, value)
21
+ @changed_attributes[attr] = old if _field_changed?(attr, old, value)
22
22
  end
23
23
  end
24
24
 
@@ -27,7 +27,7 @@ module ActiveRecord
27
27
  return
28
28
  end
29
29
 
30
- @primary_keys = keys.map { |k| k.to_sym }.to_composite_keys
30
+ @primary_keys = keys.map { |k| k.to_s }.to_composite_keys
31
31
 
32
32
  class_eval <<-EOV
33
33
  extend CompositeClassMethods
@@ -70,7 +70,7 @@ module ActiveRecord
70
70
 
71
71
  @changed_attributes = {}
72
72
  self.class.column_defaults.each do |attr, orig_value|
73
- @changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
73
+ @changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
74
74
  end
75
75
 
76
76
  @aggregation_cache = {}
@@ -5,12 +5,12 @@ module ActiveModel
5
5
  end
6
6
 
7
7
  def primary_key_changed?
8
- !!changed.detect { |key| ids_hash.keys.include?(key.to_sym) }
8
+ !!changed.detect { |key| ids_hash.keys.include?(key.to_s) }
9
9
  end
10
10
 
11
11
  def primary_key_was
12
12
  ids_hash.keys.inject(Hash.new) do |result, attribute_name|
13
- result[attribute_name.to_sym] = attribute_was(attribute_name.to_s)
13
+ result[attribute_name] = attribute_was(attribute_name.to_s)
14
14
  result
15
15
  end
16
16
  end
@@ -10,24 +10,20 @@ module ActiveRecord
10
10
  #column = self.class.columns_hash[pk]
11
11
  #substitute = connection.substitute_at(column, 0)
12
12
 
13
+ where_hash = {}
13
14
  primary_keys = Array(self.class.primary_key)
14
- bind_values = Array.new
15
- eq_predicates = Array.new
16
- primary_keys.each_with_index do |key, i|
17
- column = self.class.columns_hash[key.to_s]
18
- bind_values << [column, self[key]]
19
- substitute = connection.substitute_at(column, i)
20
- eq_predicates << self.class.arel_table[key].eq(substitute)
21
- end
22
- predicate = Arel::Nodes::And.new(eq_predicates)
23
- relation = self.class.unscoped.where(predicate)
24
15
 
25
16
  #relation = self.class.unscoped.where(
26
17
  # self.class.arel_table[pk].eq(substitute))
27
18
 
28
- # CPK
19
+ primary_keys.each do |key|
20
+ where_hash[key.to_s] = self[key]
21
+ end
22
+
23
+ # CPK
29
24
  #relation.bind_values = [[column, id]]
30
- relation.bind_values = bind_values
25
+
26
+ relation = self.class.unscoped.where(where_hash)
31
27
  relation.delete_all
32
28
  end
33
29
 
@@ -35,6 +31,33 @@ module ActiveRecord
35
31
  freeze
36
32
  end
37
33
 
34
+ def touch(name = nil)
35
+ attributes = timestamp_attributes_for_update_in_model
36
+ attributes << name if name
37
+
38
+ unless attributes.empty?
39
+ current_time = current_time_from_proper_timezone
40
+ changes = {}
41
+
42
+ attributes.each do |column|
43
+ column = column.to_s
44
+ changes[column] = write_attribute(column, current_time)
45
+ end
46
+
47
+ changes[self.class.locking_column] = increment_lock if locking_enabled?
48
+
49
+ @changed_attributes.except!(*changes.keys)
50
+
51
+ relation = self.class.send(:relation)
52
+ arel_table = self.class.arel_table
53
+ primary_key = self.class.primary_key
54
+
55
+ primary_key_predicate = relation.cpk_id_predicate(arel_table, Array(primary_key), Array(id))
56
+
57
+ self.class.unscoped.where(primary_key_predicate).update_all(changes) == 1
58
+ end
59
+ end
60
+
38
61
  def update(attribute_names = @attributes.keys)
39
62
  klass = self.class
40
63
  if !self.composite?
@@ -56,4 +79,4 @@ module ActiveRecord
56
79
  klass.connection.update stmt.to_sql
57
80
  end
58
81
  end
59
- end
82
+ end
@@ -2,6 +2,7 @@ module ActiveRecord
2
2
  class Relation
3
3
  def add_cpk_support
4
4
  class << self
5
+ include CompositePrimaryKeys::ActiveRecord::Batches
5
6
  include CompositePrimaryKeys::ActiveRecord::Calculations
6
7
  include CompositePrimaryKeys::ActiveRecord::FinderMethods
7
8
  include CompositePrimaryKeys::ActiveRecord::QueryMethods
@@ -0,0 +1,63 @@
1
+ module CompositePrimaryKeys
2
+ module ActiveRecord
3
+ module Batches
4
+ def find_in_batches(options = {})
5
+ relation = self
6
+
7
+ unless arel.orders.blank? && arel.taken.blank?
8
+ ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
9
+ end
10
+
11
+ if (finder_options = options.except(:start, :batch_size)).present?
12
+ raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
13
+ raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
14
+
15
+ relation = apply_finder_options(finder_options)
16
+ end
17
+
18
+ start = options.delete(:start).to_i
19
+ batch_size = options.delete(:batch_size) || 1000
20
+
21
+ relation = relation.reorder(batch_order).limit(batch_size)
22
+
23
+ # CPK
24
+ #records = relation.where(table[primary_key].gteq(start)).all
25
+ self.primary_key.each do |key|
26
+ relation = relation.where(table[key].gteq(start))
27
+ end
28
+ records = relation.all
29
+
30
+ while records.any?
31
+ records_size = records.size
32
+ primary_key_offset = records.last.id
33
+
34
+ yield records
35
+
36
+ break if records_size < batch_size
37
+
38
+ if primary_key_offset
39
+ # CPK
40
+ #records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
41
+ self.primary_key.each do |key|
42
+ relation = relation.where(table[key].gt(primary_key_offset))
43
+ end
44
+ records = relation.to_a
45
+
46
+ else
47
+ raise "Primary key not included in the custom select clause"
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def batch_order
55
+ # CPK
56
+ #"#{quoted_table_name}.#{quoted_primary_key} ASC"
57
+ self.primary_key.map do |key|
58
+ "#{quoted_table_name}.#{key} ASC"
59
+ end.join(",")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -2,7 +2,7 @@ module CompositePrimaryKeys
2
2
  module VERSION
3
3
  MAJOR = 5
4
4
  MINOR = 0
5
- TINY = 9
5
+ TINY = 10
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
8
8
  end
@@ -18,14 +18,18 @@ create table reference_codes (
18
18
  create table products (
19
19
  id serial not null,
20
20
  name varchar(50) default null,
21
- primary key (id)
21
+ primary key (id),
22
+ created_at timestamp without time zone NOT NULL,
23
+ updated_at timestamp without time zone NOT NULL
22
24
  );
23
25
 
24
26
  create table tariffs (
25
27
  tariff_id int not null,
26
28
  start_date date not null,
27
29
  amount int default null,
28
- primary key (tariff_id, start_date)
30
+ primary key (tariff_id, start_date),
31
+ created_at timestamp without time zone NOT NULL,
32
+ updated_at timestamp without time zone NOT NULL
29
33
  );
30
34
 
31
35
  create table product_tariffs (
@@ -16,13 +16,17 @@ create table reference_codes (
16
16
 
17
17
  create table products (
18
18
  id int(11) not null primary key,
19
- name varchar(50) default null
19
+ name varchar(50) default null,
20
+ created_at TIMESTAMP,
21
+ updated_at TIMESTAMP
20
22
  );
21
23
 
22
24
  create table tariffs (
23
25
  tariff_id int(11) not null,
24
26
  start_date date not null,
25
27
  amount integer(11) default null,
28
+ created_at TIMESTAMP,
29
+ updated_at TIMESTAMP,
26
30
  primary key (tariff_id, start_date)
27
31
  );
28
32
 
data/test/test_find.rb CHANGED
@@ -2,7 +2,7 @@ require File.expand_path('../abstract_unit', __FILE__)
2
2
 
3
3
  # Testing the find action on composite ActiveRecords with two primary keys
4
4
  class TestFind < ActiveSupport::TestCase
5
- fixtures :capitols, :reference_types, :reference_codes, :suburbs
5
+ fixtures :capitols, :departments, :reference_types, :reference_codes, :suburbs
6
6
 
7
7
  def test_find_first
8
8
  ref_code = ReferenceCode.find(:first, :order => 'reference_type_id, reference_code')
@@ -75,4 +75,10 @@ class TestFind < ActiveSupport::TestCase
75
75
  suburb = Suburb.find(:last, :order => 'suburbs.city_id DESC')
76
76
  assert_equal([1,1], suburb.id)
77
77
  end
78
+
79
+ def test_find_in_batchs
80
+ departments = Department.find_in_batches do |batch|
81
+ assert_equal(2, batch.size)
82
+ end
83
+ end
78
84
  end
data/test/test_ids.rb CHANGED
@@ -78,7 +78,7 @@ class TestIds < ActiveSupport::TestCase
78
78
  testing_with do
79
79
  if composite?
80
80
  assert_not_nil @klass.primary_keys
81
- assert_equal @primary_keys.map {|key| key.to_sym}, @klass.primary_keys
81
+ assert_equal @primary_keys.map {|key| key.to_s}, @klass.primary_keys
82
82
  assert_equal @klass.primary_keys, @klass.primary_key
83
83
  assert_kind_of(CompositePrimaryKeys::CompositeKeys, @klass.primary_keys)
84
84
  assert_equal @primary_keys.map {|key| key.to_sym}.join(','), @klass.primary_key.to_s
@@ -91,7 +91,7 @@ class TestIds < ActiveSupport::TestCase
91
91
  end
92
92
 
93
93
  def test_inherited_primary_keys
94
- assert_equal([:reference_type_id, :reference_code], ChildCpkTest.primary_keys)
94
+ assert_equal(["reference_type_id", "reference_code"], ChildCpkTest.primary_keys)
95
95
  end
96
96
 
97
97
  def test_inherited_ids
@@ -0,0 +1,23 @@
1
+ # Test cases devised by Santiago that broke the Composite Primary Keys
2
+ # code at one point in time. But no more!!!
3
+ require File.expand_path('../abstract_unit', __FILE__)
4
+
5
+ class TestTouch < ActiveSupport::TestCase
6
+ fixtures :products, :tariffs
7
+
8
+ def test_touching_a_record_updates_its_timestamp
9
+ tariff = tariffs(:flat)
10
+ previous_amount = tariff.amount
11
+ previously_updated_at = tariff.updated_at
12
+
13
+ tariff.amount = previous_amount + 1
14
+ tariff.touch
15
+
16
+ assert_not_equal previously_updated_at, tariff.updated_at
17
+ assert_equal previous_amount + 1, tariff.amount
18
+ assert tariff.amount_changed?, 'tarif amount should have changed'
19
+ assert tariff.changed?, 'tarif should be marked as changed'
20
+ tariff.reload
21
+ assert_not_equal previously_updated_at, tariff.updated_at
22
+ end
23
+ end
data/test/test_update.rb CHANGED
@@ -41,13 +41,13 @@ class TestUpdate < ActiveSupport::TestCase
41
41
  obj.reference_type_id = 2
42
42
  obj.reference_code = 3
43
43
  assert(obj.primary_key_changed?)
44
- assert_equal({:reference_type_id => 1, :reference_code => 1}, obj.primary_key_was)
45
- assert_equal({:reference_type_id => 2, :reference_code => 3}, obj.ids_hash)
44
+ assert_equal({"reference_type_id" => 1, "reference_code" => 1}, obj.primary_key_was)
45
+ assert_equal({"reference_type_id" => 2, "reference_code" => 3}, obj.ids_hash)
46
46
  assert(obj.save)
47
47
  assert(obj.reload)
48
48
  assert_equal(2, obj.reference_type_id)
49
49
  assert_equal(3, obj.reference_code)
50
- assert_equal({:reference_type_id => 2, :reference_code => 3}, obj.ids_hash)
50
+ assert_equal({"reference_type_id" => 2, "reference_code" => 3}, obj.ids_hash)
51
51
  assert_equal([2, 3], obj.id)
52
52
  end
53
53
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composite_primary_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.9
4
+ version: 5.0.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-09-29 00:00:00.000000000 Z
13
+ date: 2012-11-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -19,7 +19,7 @@ dependencies:
19
19
  requirements:
20
20
  - - ! '>='
21
21
  - !ruby/object:Gem::Version
22
- version: 3.2.8
22
+ version: 3.2.9
23
23
  - - ~>
24
24
  - !ruby/object:Gem::Version
25
25
  version: 3.2.0
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ! '>='
32
32
  - !ruby/object:Gem::Version
33
- version: 3.2.8
33
+ version: 3.2.9
34
34
  - - ~>
35
35
  - !ruby/object:Gem::Version
36
36
  version: 3.2.0
@@ -70,6 +70,7 @@ files:
70
70
  - lib/composite_primary_keys/dirty.rb
71
71
  - lib/composite_primary_keys/fixtures.rb
72
72
  - lib/composite_primary_keys/persistence.rb
73
+ - lib/composite_primary_keys/relation/batches.rb
73
74
  - lib/composite_primary_keys/relation/calculations.rb
74
75
  - lib/composite_primary_keys/relation/finder_methods.rb
75
76
  - lib/composite_primary_keys/relation/query_methods.rb
@@ -187,6 +188,7 @@ files:
187
188
  - test/test_predicates.rb
188
189
  - test/test_santiago.rb
189
190
  - test/test_suite.rb
191
+ - test/test_touch.rb
190
192
  - test/test_tutorial_example.rb
191
193
  - test/test_update.rb
192
194
  - test/test_validations.rb
@@ -210,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
210
212
  version: '0'
211
213
  requirements: []
212
214
  rubyforge_project: compositekeys
213
- rubygems_version: 1.8.23
215
+ rubygems_version: 1.8.24
214
216
  signing_key:
215
217
  specification_version: 3
216
218
  summary: Composite key support for ActiveRecord
@@ -240,6 +242,7 @@ test_files:
240
242
  - test/test_predicates.rb
241
243
  - test/test_santiago.rb
242
244
  - test/test_suite.rb
245
+ - test/test_touch.rb
243
246
  - test/test_tutorial_example.rb
244
247
  - test/test_update.rb
245
248
  - test/test_validations.rb