composite_primary_keys 8.0.0 → 8.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bb06f6e03a4f6976e562dc14e7943508372809c3
4
- data.tar.gz: bb48d494707c41060e43cd7c1d4af62f6f20c70a
3
+ metadata.gz: fbf168474ed65f8ab1b123eec93d3e63fa218e8a
4
+ data.tar.gz: 4e3168853f10d4bd5fd564e6beb27972dbee0588
5
5
  SHA512:
6
- metadata.gz: 3d86a6f26ddac7fd14728862eb0a012efc0d6e4746e4d27f715097f6863c4412816c924278ec58b97220671fc7e8434e1b608df6a5365623d7889323d427cce7
7
- data.tar.gz: dcca7b514f7f8748c6df1f8ac31d84491a6c9893daa21157413c528f5597fe008a99bf69ab57da684c1ea999b7d9c842b2cd3a060bcee342f06e1b48dc751a16
6
+ metadata.gz: 5e486302769c4f5fab7270a6f3ca684bfdbf1826aa7ae16d9a11b0d4883ab97d93361bb22dff69389c1f2eabc133a201b4f1a4b1150b371f73d4bb16f5d32cb4
7
+ data.tar.gz: 2d740b4e57d3f206956b202cfbde77b0acb550da97c14d717369c06666c1dfa9098ad8a5159bfa40826f124161c637870b3a61d624a68d5e83ae7276571e3d73
@@ -1,7 +1,26 @@
1
- == 8.0.0 (2014-01-?)
1
+ == 8.1.0 (2014-03-23)
2
+
3
+ * ActiveRecord 4.2.1 support (Charlie Savage)
4
+ * Change parsing of composite ids to fix #290 (Charlie Savage)
5
+ * Add sqlserver setting for test suite (Joachim Herb)
6
+ * Fix sqlserver adapater, isse #224 (Joachim Herb)
7
+ * Update readme file to include version 8.* information (David Silva)
8
+
9
+
10
+ == 8.0.1 (2014-01-24)
11
+
12
+ * Support optimistic lock and lock_version added to existing fixtures (Kirika)
13
+
14
+ == 8.0.0 (2014-01-10)
2
15
 
3
16
  * ActiveRecord 4.2.* support (Sammy Larbi)
4
17
 
18
+ == 7.0.13 (2015-01-24)
19
+
20
+ * Support optimistic lock and lock_version added to existing fixtures (Kirika)
21
+ * README change to convey finding available versions (Aaron Bartell)
22
+ * Fixes indentation in product_tariffs (Zaldabus)
23
+
5
24
  == 7.0.12 (2014-11-09)
6
25
 
7
26
  * ActiveRecord 4.1.7 support (Tom Hughes)
@@ -88,6 +107,12 @@ The first one no longer works. It was removed because it made the internal code
88
107
  and makes the intention of the code clearer (especially when finding multiple records).
89
108
  If this change causes too much pain then please submit a ticket on Github.
90
109
 
110
+ == 6.0.08 (2015-01-24)
111
+
112
+ * Fix habtm association #delete_records (Uros Jurglic)
113
+ * Support optimistic locking (Toshio Maki)
114
+ * Remove singleton classes on CPK relations (Nicolás Hock Isaza)
115
+
91
116
  == 6.0.7 (2014-10-06)
92
117
 
93
118
  * Support Rails 4.0.6 (Tom Hughes)
@@ -20,6 +20,7 @@ Every major version of ActiveRecord has included numerous internal changes. As
20
20
  CPK has to be rewritten for each version of ActiveRecord. To help keep
21
21
  things straight, here is the mapping:
22
22
 
23
+ Version 8.x is designed to work with ActiveRecord 4.2.x
23
24
  Version 7.x is designed to work with ActiveRecord 4.1.x
24
25
  Version 6.x is designed to work with ActiveRecord 4.0.x
25
26
  Version 5.x is designed to work with ActiveRecord 3.2.x
@@ -26,7 +26,7 @@ $:.unshift(File.dirname(__FILE__)) unless
26
26
 
27
27
  unless defined?(ActiveRecord)
28
28
  require 'rubygems'
29
- gem 'activerecord', '4.2.0'
29
+ gem 'activerecord', '~>4.2.0'
30
30
  require 'active_record'
31
31
  end
32
32
 
@@ -54,6 +54,7 @@ require 'active_record/attribute_set/builder'
54
54
  require 'active_record/attribute_methods/primary_key'
55
55
  require 'active_record/attribute_methods/read'
56
56
  require 'active_record/attribute_methods/write'
57
+ require 'active_record/locking/optimistic'
57
58
  require 'active_record/nested_attributes'
58
59
 
59
60
  require 'active_record/connection_adapters/abstract_adapter'
@@ -97,6 +98,7 @@ require 'composite_primary_keys/attribute_methods/primary_key'
97
98
  require 'composite_primary_keys/attribute_methods/dirty'
98
99
  require 'composite_primary_keys/attribute_methods/read'
99
100
  require 'composite_primary_keys/attribute_methods/write'
101
+ require 'composite_primary_keys/locking/optimistic'
100
102
  require 'composite_primary_keys/nested_attributes'
101
103
 
102
104
  require 'composite_primary_keys/connection_adapters/abstract_adapter'
@@ -3,7 +3,10 @@ module CompositePrimaryKeys
3
3
  extend ActiveSupport::Concern
4
4
  included do
5
5
  def get_records_with_cpk_support
6
- cpk_applies = (target && target.composite?) || (owner && owner.composite?)
6
+ cpk_applies = (target && target.composite?) ||
7
+ (owner && owner.composite?) ||
8
+ (options[:primary_key] && options[:primary_key].kind_of?(Array)) ||
9
+ (options[:foreign_key] && options[:foreign_key].kind_of?(Array))
7
10
  return scope.limit(1).to_a if cpk_applies
8
11
  get_records_without_cpk_support
9
12
  end
@@ -12,4 +15,4 @@ module CompositePrimaryKeys
12
15
  end
13
16
  end
14
17
 
15
- ActiveRecord::Associations::SingularAssociation.send(:include, CompositePrimaryKeys::SingularAssociation)
18
+ ActiveRecord::Associations::SingularAssociation.send(:include, CompositePrimaryKeys::SingularAssociation)
@@ -3,16 +3,21 @@ module ActiveRecord
3
3
  module Read
4
4
  def read_attribute(attr_name, &block)
5
5
  # CPK
6
- # name = attr_name.to_s
7
- # name = self.class.primary_key if name == 'id'
8
- # @attributes.fetch_value(name, &block)
9
-
10
6
  if attr_name.kind_of?(Array)
11
- attr_name.map {|name| read_attribute(name)}.to_composite_keys
7
+ _read_attribute(attr_name, &block)
12
8
  else
13
9
  name = attr_name.to_s
14
- name = self.class.primary_key if name == 'id' && !composite?
15
- @attributes.fetch_value(name, &block)
10
+ name = self.class.primary_key if name == 'id'
11
+ _read_attribute(name, &block)
12
+ end
13
+ end
14
+
15
+ def _read_attribute(attr_name)
16
+ # CPK
17
+ if attr_name.kind_of?(Array)
18
+ attr_name.map {|name| @attributes.fetch_value(name.to_s)}
19
+ else
20
+ @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
16
21
  end
17
22
  end
18
23
  end
@@ -8,8 +8,21 @@ module CompositePrimaryKeys
8
8
  end
9
9
  end
10
10
 
11
+ def self.normalize(ids)
12
+ ids.map do |id|
13
+ if id.is_a?(Array)
14
+ normalize(id)
15
+ elsif id.is_a?(String) && id.index(ID_SEP)
16
+ id.split(ID_SEP)
17
+ else
18
+ id
19
+ end
20
+ end
21
+ end
22
+
11
23
  class CompositeKeys < Array
12
- def self.parse(value)
24
+
25
+ def self.parse(value)
13
26
  case value
14
27
  when Array
15
28
  value.to_composite_keys
@@ -4,6 +4,9 @@ module ActiveRecord
4
4
  if (adapter.to_s =~ /postgresql/) or (adapter.to_s =~ /postgis/)
5
5
  require "composite_primary_keys/connection_adapters/postgresql_adapter.rb"
6
6
  end
7
+ if (adapter.to_s =~ /sqlserver/)
8
+ require "composite_primary_keys/connection_adapters/sqlserver_adapter.rb"
9
+ end
7
10
  end
8
11
 
9
12
  def self.establish_connection(spec = ENV["DATABASE_URL"])
@@ -0,0 +1,17 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class SQLServerAdapter
4
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
5
+ sql = if pk && self.class.use_output_inserted
6
+ # support composite primary keys consisting of more than one column name
7
+ quoted_pks = [pk].flatten.map {|pk| "INSERTED.#{SQLServer::Utils.extract_identifiers(pk).quoted}"}
8
+ sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{quoted_pks.join(", ")}"
9
+ # p sql
10
+ else
11
+ "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
12
+ end
13
+ [sql, binds]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -16,54 +16,46 @@ module ActiveRecord
16
16
 
17
17
  super
18
18
  end
19
-
20
-
21
- end
22
- end
23
-
24
-
25
- module CompositePrimaryKeys
26
- module ActiveRecordCoreConcernIncludedExtension
27
- extend ActiveSupport::Concern
28
19
 
29
- included do
30
- def self.find(*ids)
31
- if composite?
32
- super(cpk_parse_ids(ids))
33
- else
34
- super
20
+ module ClassMethods
21
+ def find(*ids) # :nodoc:
22
+ # We don't have cache keys for this stuff yet
23
+ return super unless ids.length == 1
24
+ # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
25
+ return super if ids.first.kind_of?(Symbol)
26
+ return super if block_given? ||
27
+ primary_key.nil? ||
28
+ default_scopes.any? ||
29
+ current_scope ||
30
+ columns_hash.include?(inheritance_column) ||
31
+ ids.first.kind_of?(Array)
32
+
33
+ # CPK
34
+ return super if self.composite?
35
+
36
+ id = ids.first
37
+ if ActiveRecord::Base === id
38
+ id = id.id
39
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
40
+ You are passing an instance of ActiveRecord::Base to `find`.
41
+ Please pass the id of the object by calling `.id`
42
+ MSG
35
43
  end
36
- end
37
-
38
- private
39
- def self.cpk_parse_ids(ids)
40
- result = []
41
- ids.each do |id|
42
- if id.is_a?(String)
43
- if id.index(",")
44
- result << [id.split(",")]
45
- else
46
- result << [id]
47
- end
48
- elsif id.is_a?(Array) && id.count > 1 && id.first.to_s.index(",")
49
- result << id.map{|subid| subid.split(",")}
50
- else
51
- result << [id]
52
- end
53
- end
54
-
55
- copy_to_find_depth, depth = result.dup, -1
56
-
57
- until copy_to_find_depth == result.flatten
58
- depth += 1
59
- copy_to_find_depth = copy_to_find_depth.flatten(1)
44
+ key = primary_key
45
+
46
+ s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
47
+ find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
48
+ where(key => params.bind).limit(1)
49
+ }
50
+ }
51
+ record = s.execute([id], self, connection).first
52
+ unless record
53
+ raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
60
54
  end
61
-
62
- result = result.flatten(depth)
63
- return result
55
+ record
56
+ rescue RangeError
57
+ raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
64
58
  end
65
59
  end
66
60
  end
67
- end
68
-
69
- ActiveRecord::Base.send(:include, CompositePrimaryKeys::ActiveRecordCoreConcernIncludedExtension)
61
+ end
@@ -0,0 +1,55 @@
1
+ module ActiveRecord
2
+ module Locking
3
+ module Optimistic
4
+ private
5
+ def _update_record(attribute_names = @attributes.keys) #:nodoc:
6
+ return super unless locking_enabled?
7
+ return 0 if attribute_names.empty?
8
+
9
+ lock_col = self.class.locking_column
10
+ previous_lock_value = send(lock_col).to_i
11
+ increment_lock
12
+
13
+ attribute_names += [lock_col]
14
+ attribute_names.uniq!
15
+
16
+ begin
17
+ relation = self.class.unscoped
18
+
19
+ if self.composite?
20
+ stmt = relation.where(
21
+ relation.cpk_id_predicate(relation.table, self.class.primary_key, id_was).and(
22
+ relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
23
+ )
24
+ ).arel.compile_update(
25
+ arel_attributes_with_values_for_update(attribute_names),
26
+ self.class.primary_key
27
+ )
28
+ else
29
+ stmt = relation.where(
30
+ relation.table[self.class.primary_key].eq(id).and(
31
+ relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
32
+ )
33
+ ).arel.compile_update(
34
+ arel_attributes_with_values_for_update(attribute_names),
35
+ self.class.primary_key
36
+ )
37
+ end
38
+
39
+ affected_rows = self.class.connection.update stmt
40
+
41
+ unless affected_rows == 1
42
+ raise ActiveRecord::StaleObjectError.new(self, "update")
43
+ end
44
+
45
+ affected_rows
46
+
47
+ # If something went wrong, revert the version.
48
+ rescue Exception
49
+ send(lock_col + '=', previous_lock_value)
50
+ raise
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -75,28 +75,32 @@ module CompositePrimaryKeys
75
75
 
76
76
  connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false
77
77
  end
78
-
78
+
79
79
  def find_with_ids(*ids)
80
- # CPK handle strings that come w/ calling to_param on CPK-enabled models
81
- ids = cpk_parse_ids(ids)
82
80
  raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
83
81
 
84
- expects_array = ids.first.kind_of?(Array)
82
+ # CPK
83
+ #expects_array = ids.first.kind_of?(Array)
84
+ ids = CompositePrimaryKeys.normalize(ids)
85
+ expects_array = ids.flatten != ids.flatten(1)
86
+
85
87
  return ids.first if expects_array && ids.first.empty?
86
88
 
87
- # CPK - don't do this, we want an array of arrays
89
+ # CPK
88
90
  #ids = ids.flatten.compact.uniq
91
+ ids = expects_array ? ids.first : ids
92
+
89
93
  case ids.size
90
94
  when 0
91
95
  raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
92
96
  when 1
93
97
  result = find_one(ids.first)
94
- # CPK
95
- # expects_array ? [ result ] : result
96
- result
98
+ expects_array ? [ result ] : result
97
99
  else
98
100
  find_some(ids)
99
101
  end
102
+ rescue RangeError
103
+ raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID"
100
104
  end
101
105
 
102
106
  def find_one(id)
@@ -1,7 +1,7 @@
1
1
  module CompositePrimaryKeys
2
2
  module VERSION
3
3
  MAJOR = 8
4
- MINOR = 0
4
+ MINOR = 1
5
5
  TINY = 0
6
6
  STRING = [MAJOR, MINOR, TINY].join('.')
7
7
  end
@@ -16,3 +16,10 @@ postgresql:
16
16
  database: composite_primary_keys_unittest
17
17
  username: postgres
18
18
  host: localhost
19
+
20
+ sqlserver:
21
+ adapter: sqlserver
22
+ database: composite_primary_keys_unittest
23
+ username: rails
24
+ host: localhost
25
+ port: 1433
@@ -108,6 +108,7 @@ create table restaurants (
108
108
  franchise_id integer not null,
109
109
  store_id integer not null,
110
110
  name varchar(100),
111
+ lock_version integer default 0,
111
112
  primary key (franchise_id, store_id)
112
113
  );
113
114
 
@@ -119,6 +119,16 @@ create table employees (
119
119
  primary key (id)
120
120
  );
121
121
 
122
+ create table salaries (
123
+ id int not null auto_increment,
124
+ employee_id int,
125
+ location_id int,
126
+ year int not null,
127
+ month int not null,
128
+ value int default null,
129
+ primary key (id)
130
+ );
131
+
122
132
  create table comments (
123
133
  id int not null auto_increment,
124
134
  person_id int default null,
@@ -138,6 +148,7 @@ create table restaurants (
138
148
  franchise_id int not null,
139
149
  store_id int not null,
140
150
  name varchar(100),
151
+ lock_version int default 0,
141
152
  primary key (franchise_id, store_id)
142
153
  );
143
154
 
@@ -146,6 +146,7 @@ create table restaurants (
146
146
  franchise_id number(11) not null,
147
147
  store_id number(11) not null,
148
148
  name varchar(100),
149
+ lock_version number(11) default 0,
149
150
  constraint restaurants_pk primary key (franchise_id, store_id)
150
151
  );
151
152
 
@@ -121,6 +121,16 @@ create table employees (
121
121
  primary key (id)
122
122
  );
123
123
 
124
+ create table salaries (
125
+ id serial not null,
126
+ employee_id int,
127
+ location_id int,
128
+ year int not null,
129
+ month int not null,
130
+ value int default null,
131
+ primary key (id)
132
+ );
133
+
124
134
  create table comments (
125
135
  id serial not null,
126
136
  person_id int default null,
@@ -140,6 +150,7 @@ create table restaurants (
140
150
  franchise_id int not null,
141
151
  store_id int not null,
142
152
  name varchar(100),
153
+ lock_version int default 0,
143
154
  primary key (franchise_id, store_id)
144
155
  );
145
156
 
@@ -112,6 +112,15 @@ create table employees (
112
112
  location_id integer null
113
113
  );
114
114
 
115
+ create table salaries (
116
+ id integer not null primary key autoincrement,
117
+ employee_id integer,
118
+ location_id integer,
119
+ year int not null,
120
+ month int not null,
121
+ value int default null
122
+ );
123
+
115
124
  create table comments (
116
125
  id integer not null primary key autoincrement,
117
126
  person_id int null,
@@ -129,6 +138,7 @@ create table restaurants (
129
138
  franchise_id integer not null,
130
139
  store_id integer not null,
131
140
  name varchar(100),
141
+ lock_version integer default 0,
132
142
  primary key (franchise_id, store_id)
133
143
  );
134
144
 
@@ -148,7 +148,8 @@ go
148
148
  CREATE TABLE restaurants (
149
149
  franchise_id [int] NOT NULL,
150
150
  store_id [int] NOT NULL,
151
- name [varchar](100)
151
+ name [varchar](100),
152
+ lock_version [int] DEFAULT 0
152
153
  CONSTRAINT [restaurants_pk] PRIMARY KEY CLUSTERED
153
154
  ( [franchise_id], [store_id] )
154
155
  );
@@ -222,4 +223,4 @@ CREATE TABLE products_restaurants (
222
223
  franchise_id [int] NOT NULL,
223
224
  store_id [int] NOT NULL
224
225
  );
225
- go
226
+ go
@@ -2,4 +2,10 @@ class Employee < ActiveRecord::Base
2
2
  belongs_to :department, :foreign_key => [:department_id, :location_id]
3
3
  has_many :comments, :as => :person
4
4
  has_and_belongs_to_many :groups
5
+ has_many :salaries,
6
+ :primary_key => [:id, :location_id],
7
+ :foreign_key => [:employee_id, :location_id]
8
+ has_one :one_salary, :class_name => "Salary",
9
+ :primary_key => [:id, :location_id],
10
+ :foreign_key => [:employee_id, :location_id]
5
11
  end
@@ -0,0 +1,5 @@
1
+ class Salary < ActiveRecord::Base
2
+ belongs_to :employee,
3
+ :primary_key => [:id, :location_id],
4
+ :foreign_key => [:employee_id, :location_id]
5
+ end
@@ -81,6 +81,21 @@ class TestAssociations < ActiveSupport::TestCase
81
81
  refute_equal accounting_head, engineering_head
82
82
  end
83
83
 
84
+ def test_has_one_association_primary_key_and_foreign_key_are_present
85
+ steve = employees(:steve)
86
+ steve_salary = steve.create_one_salary(year: "2015", month: "1")
87
+
88
+ jill = employees(:jill)
89
+ jill_salary = jill.create_one_salary(year: "2015", month: "1")
90
+
91
+ steve_salary.reload
92
+ jill_salary.reload
93
+ assert_equal(steve.id, steve_salary.employee_id)
94
+ assert_equal(1, steve_salary.location_id)
95
+ assert_equal(jill.id, jill_salary.employee_id)
96
+ assert_equal(1, jill_salary.location_id)
97
+ end
98
+
84
99
  def test_has_many_association_is_not_cached_to_where_it_returns_the_wrong_ones
85
100
  engineering = departments(:engineering)
86
101
  engineering_employees = engineering.employees
@@ -91,6 +106,36 @@ class TestAssociations < ActiveSupport::TestCase
91
106
  refute_equal accounting_employees, engineering_employees
92
107
  end
93
108
 
109
+ def test_has_many_association_primary_key_and_foreign_key_are_present
110
+ steve = employees(:steve)
111
+ steve_salary = steve.salaries.create(year: 2015, month: 1)
112
+
113
+ jill = employees(:jill)
114
+ jill_salary = jill.salaries.create(year: 2015, month: 1)
115
+
116
+ steve_salary.reload
117
+ jill_salary.reload
118
+ assert_equal(steve.id, steve_salary.employee_id)
119
+ assert_equal(1, steve_salary.location_id)
120
+ assert_equal(jill.id, jill_salary.employee_id)
121
+ assert_equal(1, jill_salary.location_id)
122
+ end
123
+
124
+ def test_belongs_to_association_primary_key_and_foreign_key_are_present
125
+ salary_01 = Salary.new(year: 2015, month: 1, employee_id: 5, location_id: 1)
126
+ employee_01 = salary_01.create_employee
127
+ salary_02 = Salary.new(year: 2015, month: 1, employee_id: 6, location_id: 1)
128
+ employee_02 = salary_02.create_employee
129
+
130
+ employee_01.reload
131
+ employee_02.reload
132
+
133
+ assert_equal(5, employee_01.id)
134
+ assert_equal(1, employee_01.location_id)
135
+ assert_equal(6, employee_02.id)
136
+ assert_equal(1, employee_02.location_id)
137
+ end
138
+
94
139
  def test_find_includes_product_tariffs_product
95
140
  # Old style
96
141
  product_tariffs = ProductTariff.includes(:product)
@@ -42,13 +42,13 @@ class TestAttributes < ActiveSupport::TestCase
42
42
  end
43
43
  end
44
44
  end
45
-
45
+
46
46
  def test_brackets_foreign_key_assignment
47
47
  tarrif = tariffs(:flat)
48
48
  product_tariff = product_tariffs(:first_flat)
49
49
  compare_indexes(tarrif, tarrif.class.primary_key, product_tariff, [:tariff_id, :tariff_start_date])
50
50
  end
51
-
51
+
52
52
  private
53
53
 
54
54
  def compare_indexes(obj1, indexes1, obj2, indexes2)
@@ -1,7 +1,7 @@
1
1
  require File.expand_path('../abstract_unit', __FILE__)
2
2
 
3
3
  class TestCreate < ActiveSupport::TestCase
4
- fixtures :reference_types, :reference_codes, :streets, :suburbs
4
+ fixtures :students, :dorms, :rooms, :room_assignments, :reference_types, :reference_codes, :streets, :suburbs
5
5
 
6
6
  CLASSES = {
7
7
  :single => {
@@ -109,4 +109,49 @@ class TestCreate < ActiveSupport::TestCase
109
109
  assert_equal(25, suburb.suburb_id)
110
110
  assert_equal("My Suburb", suburb.name)
111
111
  end
112
+
113
+ def test_has_many_ids_1
114
+ dorm = dorms(:toyon)
115
+ room = Room.new(:dorm_id => dorm.id, :room_id => 5)
116
+ room.save!
117
+
118
+ student1 = students(:kelly)
119
+ student2 = students(:jordan)
120
+
121
+ RoomAssignment.delete_all
122
+
123
+ assignment1 = RoomAssignment.new(:student_id => student1.id, :dorm_id => room.dorm_id, :room_id => room.room_id)
124
+ assignment1.save!
125
+
126
+ room.room_assignment_ids = [[assignment1.student_id, assignment1.dorm_id, assignment1.room_id]]
127
+ room.save!
128
+
129
+ assert_equal(1, room.room_assignments.length)
130
+ assert_equal(assignment1, room.room_assignments.first)
131
+ end
132
+
133
+ def test_has_many_ids_2
134
+ dorm = dorms(:toyon)
135
+ room = Room.new(:dorm_id => dorm.id, :room_id => 5)
136
+ room.save!
137
+
138
+ student1 = students(:kelly)
139
+ student2 = students(:jordan)
140
+
141
+ RoomAssignment.delete_all
142
+
143
+ assignment1 = RoomAssignment.new(:student_id => student1.id, :dorm_id => room.dorm_id, :room_id => room.room_id)
144
+ assignment1.save!
145
+
146
+ assignment2 = RoomAssignment.new(:student_id => student2.id, :dorm_id => room.dorm_id, :room_id => room.room_id)
147
+ assignment2.save!
148
+
149
+ room.room_assignment_ids = [[assignment1.student_id, assignment1.dorm_id, assignment1.room_id],
150
+ [assignment2.student_id, assignment2.dorm_id, assignment2.room_id]]
151
+ room.save!
152
+
153
+ assert_equal(2, room.room_assignments.length)
154
+ assert_equal(assignment1, room.room_assignments[0])
155
+ assert_equal(assignment2, room.room_assignments[1])
156
+ end
112
157
  end
@@ -97,6 +97,16 @@ class TestDelete < ActiveSupport::TestCase
97
97
  assert_equal records_after, records_before - steve.groups.count
98
98
  end
99
99
 
100
+ def test_destroy_has_and_belongs_to_many_on_non_cpk
101
+ records_before = ActiveRecord::Base.connection.execute("select * from employees_groups").count
102
+ employee = Employee.create
103
+ employee.groups << Group.create(name: 'test')
104
+ employees_groups_count = employee.groups.count
105
+ employee.destroy!
106
+ records_after = ActiveRecord::Base.connection.execute("select * from employees_groups").count
107
+ assert_equal records_before, records_after
108
+ end
109
+
100
110
  def test_delete_not_destroy_on_cpk
101
111
  tariff = Tariff.where(tariff_id: 2).first
102
112
  tariff.delete
@@ -0,0 +1,18 @@
1
+ require File.expand_path('../abstract_unit', __FILE__)
2
+
3
+ class TestOptimisitic < ActiveSupport::TestCase
4
+ fixtures :restaurants
5
+
6
+ def test_update_with_stale_error
7
+ restaurant_1 = Restaurant.find([1, 1])
8
+ restaurant_1['name'] = "McDonalds renamed"
9
+
10
+ restaurant_2 = Restaurant.find([1, 1])
11
+ restaurant_2['name'] = "McDonalds renamed 2"
12
+
13
+ assert(restaurant_1.save)
14
+ assert_raise ActiveRecord::StaleObjectError do
15
+ restaurant_2.save
16
+ end
17
+ end
18
+ end
@@ -19,6 +19,7 @@
19
19
  test_ids
20
20
  test_miscellaneous
21
21
  test_nested_attributes
22
+ test_optimistic
22
23
  test_pagination
23
24
  test_polymorphic
24
25
  test_predicates
@@ -4,19 +4,23 @@ class TestTutorialExample < ActiveSupport::TestCase
4
4
  fixtures :users, :groups, :memberships, :membership_statuses
5
5
 
6
6
  def test_membership
7
- assert(membership = Membership.find([1, 1]), "Cannot find a membership")
7
+ membership = Membership.find([1, 1])
8
+ assert(membership, "Cannot find a membership")
8
9
  assert(membership.user)
9
10
  assert(membership.group)
10
11
  end
11
12
 
12
13
  def test_status
13
- assert(membership = Membership.find([1, 1]), "Cannot find a membership")
14
- assert(statuses = membership.statuses, "No has_many association to status")
14
+ membership = Membership.find([1, 1])
15
+ statuses = membership.statuses
16
+ assert(membership, "Cannot find a membership")
17
+ assert(statuses, "No has_many association to status")
15
18
  assert_equal(membership, statuses.first.membership)
16
19
  end
17
20
 
18
21
  def test_count
19
- assert(membership = Membership.find([1, 1]), "Cannot find a membership")
22
+ membership = Membership.find([1, 1])
23
+ assert(membership, "Cannot find a membership")
20
24
  assert_equal(1, membership.statuses.count)
21
25
  end
22
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composite_primary_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0
4
+ version: 8.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charlie Savage
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-10 00:00:00.000000000 Z
11
+ date: 2015-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -58,9 +58,11 @@ files:
58
58
  - lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb
59
59
  - lib/composite_primary_keys/connection_adapters/abstract_adapter.rb
60
60
  - lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb
61
+ - lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb
61
62
  - lib/composite_primary_keys/core.rb
62
63
  - lib/composite_primary_keys/dirty.rb
63
64
  - lib/composite_primary_keys/fixtures.rb
65
+ - lib/composite_primary_keys/locking/optimistic.rb
64
66
  - lib/composite_primary_keys/model_schema.rb
65
67
  - lib/composite_primary_keys/nested_attributes.rb
66
68
  - lib/composite_primary_keys/persistence.rb
@@ -149,6 +151,7 @@ files:
149
151
  - test/fixtures/room_attribute_assignments.yml
150
152
  - test/fixtures/room_attributes.yml
151
153
  - test/fixtures/rooms.yml
154
+ - test/fixtures/salary.rb
152
155
  - test/fixtures/seat.rb
153
156
  - test/fixtures/seats.yml
154
157
  - test/fixtures/street.rb
@@ -189,6 +192,7 @@ files:
189
192
  - test/test_ids.rb
190
193
  - test/test_miscellaneous.rb
191
194
  - test/test_nested_attributes.rb
195
+ - test/test_optimistic.rb
192
196
  - test/test_pagination.rb
193
197
  - test/test_polymorphic.rb
194
198
  - test/test_predicates.rb
@@ -249,6 +253,7 @@ test_files:
249
253
  - test/test_ids.rb
250
254
  - test/test_miscellaneous.rb
251
255
  - test/test_nested_attributes.rb
256
+ - test/test_optimistic.rb
252
257
  - test/test_pagination.rb
253
258
  - test/test_polymorphic.rb
254
259
  - test/test_predicates.rb