composite_primary_keys 5.0.9 → 5.0.10

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/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