composite_primary_keys 2.3.2 → 2.3.5

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.
@@ -1,3 +1,7 @@
1
+ == 2.3.5 2009-12-16
2
+
3
+ * Fixed several bugs in has_one and has_many associations when :primary_key specified [kpumuk]
4
+
1
5
  == 2.3.2 2009-07-16
2
6
 
3
7
  * explicitly load associations.rb due to some getting an unitialized constant error
@@ -46,7 +50,7 @@
46
50
 
47
51
  == 1.0.4 2008-07-15
48
52
 
49
- * support for oracle_enhanced adapter [thx Raimonds Simanovskis]
53
+ * support for oracle_enhanced adapter [thx Raimonds Simanovskis]
50
54
 
51
55
  == 1.0.3 2008-07-13
52
56
 
@@ -116,7 +120,7 @@
116
120
  == 0.8.4 / 2007-5-3
117
121
 
118
122
  * 1 bugfix
119
- * Corrected ids_list => ids in the exception message. That'll teach me for not adding unit tests before fixing bugs.
123
+ * Corrected ids_list => ids in the exception message. That'll teach me for not adding unit tests before fixing bugs.
120
124
 
121
125
  == 0.8.3 / 2007-5-3
122
126
 
@@ -154,11 +158,11 @@
154
158
  == 0.1.4
155
159
  * it was important that #{primary_key} for composites --> 'key1,key2' and not 'key1key2' so created PrimaryKeys class
156
160
 
157
- == 0.0.1
161
+ == 0.0.1
158
162
  * Initial version
159
163
  * set_primary_keys(*keys) is the activation class method to transform an ActiveRecord into a composite primary key AR
160
- * find(*ids) supports the passing of
161
- * id sets: Foo.find(2,1),
162
- * lists of id sets: Foo.find([2,1], [7,3], [8,12]),
164
+ * find(*ids) supports the passing of
165
+ * id sets: Foo.find(2,1),
166
+ * lists of id sets: Foo.find([2,1], [7,3], [8,12]),
163
167
  * and even stringified versions of the above:
164
168
  * Foo.find '2,1' or Foo.find '2,1;7,3'
@@ -24,6 +24,7 @@ lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb
24
24
  lib/composite_primary_keys/fixtures.rb
25
25
  lib/composite_primary_keys/migration.rb
26
26
  lib/composite_primary_keys/reflection.rb
27
+ lib/composite_primary_keys/validations/uniqueness.rb
27
28
  lib/composite_primary_keys/version.rb
28
29
  loader.rb
29
30
  local/database_connections.rb.sample
data/Rakefile CHANGED
@@ -1,65 +1,65 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/clean'
4
- require 'rake/testtask'
5
- require 'rake/rdoctask'
6
- require 'rake/packagetask'
7
- require 'rake/gempackagetask'
8
- require 'rake/contrib/rubyforgepublisher'
9
- require 'fileutils'
10
- require 'hoe'
11
- include FileUtils
12
- require File.join(File.dirname(__FILE__), 'lib', 'composite_primary_keys', 'version')
13
-
14
- AUTHOR = "Dr Nic Williams"
15
- EMAIL = "drnicwilliams@gmail.com"
16
- DESCRIPTION = "Composite key support for ActiveRecords"
17
- GEM_NAME = "composite_primary_keys" # what ppl will type to install your gem
18
- if File.exists?("~/.rubyforge/user-config.yml")
19
- # TODO this should prob go in a local/ file
20
- config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
21
- RUBYFORGE_USERNAME = config["username"]
22
- end
23
- RUBYFORGE_PROJECT = "compositekeys"
24
- HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
25
-
26
- REV = nil #File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil
27
- VERS = ENV['VERSION'] || (CompositePrimaryKeys::VERSION::STRING + (REV ? ".#{REV}" : ""))
28
- CLEAN.include ['**/.*.sw?', '*.gem', '.config','debug.log','*.db','logfile','log/**/*','**/.DS_Store', '.project']
29
- RDOC_OPTS = ['--quiet', '--title', "newgem documentation",
30
- "--opname", "index.html",
31
- "--line-numbers",
32
- "--main", "README",
33
- "--inline-source"]
34
-
35
- class Hoe
36
- def extra_deps
37
- @extra_deps.reject { |x| Array(x).first == 'hoe' }
38
- end
39
- end
40
-
41
- # Generate all the Rake tasks
42
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
43
- hoe = Hoe.new(GEM_NAME, VERS) do |p|
44
- p.author = AUTHOR
45
- p.description = DESCRIPTION
46
- p.email = EMAIL
47
- p.summary = DESCRIPTION
48
- p.url = HOMEPATH
49
- p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
50
- p.test_globs = ["test/**/test*.rb"]
51
- p.clean_globs |= CLEAN #An array of file patterns to delete on clean.
52
-
53
- # == Optional
54
- p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
55
- p.extra_deps = [['activerecord', '>= 2.3.2']] #An array of rubygem dependencies.
56
- #p.spec_extras - A hash of extra values to set in the gemspec.
57
- end
58
-
59
- CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\n\n")
60
- PATH = RUBYFORGE_PROJECT
61
- hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
62
-
63
- PROJECT_ROOT = File.expand_path(".")
64
-
65
- require 'loader'
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+ require 'rake/packagetask'
7
+ require 'rake/gempackagetask'
8
+ require 'rake/contrib/rubyforgepublisher'
9
+ require 'fileutils'
10
+ require 'hoe'
11
+ include FileUtils
12
+ require File.join(File.dirname(__FILE__), 'lib', 'composite_primary_keys', 'version')
13
+
14
+ AUTHOR = "Dr Nic Williams"
15
+ EMAIL = "drnicwilliams@gmail.com"
16
+ DESCRIPTION = "Composite key support for ActiveRecords"
17
+ GEM_NAME = "composite_primary_keys" # what ppl will type to install your gem
18
+ if File.exists?("~/.rubyforge/user-config.yml")
19
+ # TODO this should prob go in a local/ file
20
+ config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
21
+ RUBYFORGE_USERNAME = config["username"]
22
+ end
23
+ RUBYFORGE_PROJECT = "compositekeys"
24
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
25
+
26
+ REV = nil #File.read(".svn/entries")[/committed-rev="(\d+)"/, 1] rescue nil
27
+ VERS = ENV['VERSION'] || (CompositePrimaryKeys::VERSION::STRING + (REV ? ".#{REV}" : ""))
28
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config','debug.log','*.db','logfile','log/**/*','**/.DS_Store', '.project']
29
+ RDOC_OPTS = ['--quiet', '--title', "newgem documentation",
30
+ "--opname", "index.html",
31
+ "--line-numbers",
32
+ "--main", "README",
33
+ "--inline-source"]
34
+
35
+ class Hoe
36
+ def extra_deps
37
+ @extra_deps.reject { |x| Array(x).first == 'hoe' }
38
+ end
39
+ end
40
+
41
+ # Generate all the Rake tasks
42
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
43
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
44
+ p.author = AUTHOR
45
+ p.description = DESCRIPTION
46
+ p.email = EMAIL
47
+ p.summary = DESCRIPTION
48
+ p.url = HOMEPATH
49
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
50
+ p.test_globs = ["test/**/test*.rb"]
51
+ p.clean_globs |= CLEAN #An array of file patterns to delete on clean.
52
+
53
+ # == Optional
54
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
55
+ p.extra_deps = [['activerecord', '>= 2.3.5']] #An array of rubygem dependencies.
56
+ #p.spec_extras - A hash of extra values to set in the gemspec.
57
+ end
58
+
59
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\n\n")
60
+ PATH = RUBYFORGE_PROJECT
61
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
62
+
63
+ PROJECT_ROOT = File.expand_path(".")
64
+
65
+ require 'loader'
@@ -44,6 +44,7 @@ require 'composite_primary_keys/base'
44
44
  require 'composite_primary_keys/calculations'
45
45
  require 'composite_primary_keys/migration'
46
46
  require 'composite_primary_keys/attribute_methods'
47
+ require 'composite_primary_keys/validations/uniqueness'
47
48
 
48
49
  ActiveRecord::Base.class_eval do
49
50
  include CompositePrimaryKeys::ActiveRecord::Base
@@ -180,11 +180,12 @@ module ActiveRecord::Associations::ClassMethods
180
180
  raise AssociationNotSupported, "Polymorphic joins not supported for composite keys"
181
181
  else
182
182
  foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
183
+ primary_key = options[:primary_key] || parent.primary_key
183
184
  " LEFT OUTER JOIN %s ON %s " % [
184
185
  table_name_and_alias,
185
186
  composite_join_clause(
186
187
  full_keys(aliased_table_name, foreign_key),
187
- full_keys(parent.aliased_table_name, parent.primary_key)),
188
+ full_keys(parent.aliased_table_name, primary_key)),
188
189
  ]
189
190
  end
190
191
  when :belongs_to
@@ -338,7 +339,7 @@ module ActiveRecord::Associations
338
339
  @finder_sql << " AND (#{conditions})" if conditions
339
340
 
340
341
  else
341
- @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, @owner.quoted_id)
342
+ @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, owner_quoted_id)
342
343
  @finder_sql << " AND (#{conditions})" if conditions
343
344
  end
344
345
 
@@ -386,7 +387,7 @@ module ActiveRecord::Associations
386
387
  "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
387
388
  "#{@reflection.klass.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
388
389
  else
389
- @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, @owner.quoted_id)
390
+ @finder_sql = full_columns_equals(@reflection.klass.table_name, @reflection.primary_key_name, owner_quoted_id)
390
391
  end
391
392
 
392
393
  @finder_sql << " AND (#{conditions})" if conditions
@@ -29,6 +29,8 @@ module CompositePrimaryKeys
29
29
  include CompositePrimaryKeys::ActiveRecord::AssociationPreload
30
30
  include CompositePrimaryKeys::ActiveRecord::Calculations
31
31
  include CompositePrimaryKeys::ActiveRecord::AttributeMethods
32
+
33
+ extend CompositePrimaryKeys::ActiveRecord::Validations::Uniqueness::ClassMethods
32
34
  EOV
33
35
  end
34
36
 
@@ -0,0 +1,77 @@
1
+ module CompositePrimaryKeys
2
+ module ActiveRecord
3
+ module Validations
4
+ module Uniqueness
5
+ module ClassMethods
6
+ def validates_uniqueness_of(*attr_names)
7
+ configuration = { :case_sensitive => true }
8
+ configuration.update(attr_names.extract_options!)
9
+
10
+ validates_each(attr_names,configuration) do |record, attr_name, value|
11
+ # The check for an existing value should be run from a class that
12
+ # isn't abstract. This means working down from the current class
13
+ # (self), to the first non-abstract class. Since classes don't know
14
+ # their subclasses, we have to build the hierarchy between self and
15
+ # the record's class.
16
+ class_hierarchy = [record.class]
17
+ while class_hierarchy.first != self
18
+ class_hierarchy.insert(0, class_hierarchy.first.superclass)
19
+ end
20
+
21
+ # Now we can work our way down the tree to the first non-abstract
22
+ # class (which has a database table to query from).
23
+ finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
24
+
25
+ column = finder_class.columns_hash[attr_name.to_s]
26
+
27
+ if value.nil?
28
+ comparison_operator = "IS ?"
29
+ elsif column.text?
30
+ comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
31
+ value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s
32
+ else
33
+ comparison_operator = "= ?"
34
+ end
35
+
36
+ sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
37
+
38
+ if value.nil? || (configuration[:case_sensitive] || !column.text?)
39
+ condition_sql = "#{sql_attribute} #{comparison_operator}"
40
+ condition_params = [value]
41
+ else
42
+ condition_sql = "LOWER(#{sql_attribute}) #{comparison_operator}"
43
+ condition_params = [value.mb_chars.downcase]
44
+ end
45
+
46
+ if scope = configuration[:scope]
47
+ Array(scope).map do |scope_item|
48
+ scope_value = record.send(scope_item)
49
+ condition_sql << " AND " << attribute_condition("#{record.class.quoted_table_name}.#{scope_item}", scope_value)
50
+ condition_params << scope_value
51
+ end
52
+ end
53
+
54
+ unless record.new_record?
55
+ if record.class.composite?
56
+ record.class.primary_keys.each do |key|
57
+ condition_sql << " AND #{record.class.quoted_table_name}.#{key} <> ?"
58
+ condition_params << record.send(key)
59
+ end
60
+ else
61
+ condition_sql << " AND #{record.class.quoted_table_name}.#{record.class.primary_key} <> ?"
62
+ condition_params << record.send(:id)
63
+ end
64
+ end
65
+
66
+ finder_class.with_exclusive_scope do
67
+ if finder_class.exists?([condition_sql, *condition_params])
68
+ record.errors.add(attr_name, :taken, :default => configuration[:message], :value => value)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,8 +1,8 @@
1
- module CompositePrimaryKeys
2
- module VERSION #:nodoc:
3
- MAJOR = 2
4
- MINOR = 3
5
- TINY = 2
6
- STRING = [MAJOR, MINOR, TINY].join('.')
7
- end
8
- end
1
+ module CompositePrimaryKeys
2
+ module VERSION #:nodoc:
3
+ MAJOR = 2
4
+ MINOR = 3
5
+ TINY = 5
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
@@ -172,3 +172,10 @@ create table room_assignments (
172
172
  room_id int(11) not null
173
173
  ) type=InnoDB;
174
174
 
175
+ create table seats (
176
+ flight_number int(11) not null,
177
+ seat int(11) not null,
178
+ customer int,
179
+ primary key (flight_number, seat)
180
+ ) type=InnoDB;
181
+
@@ -197,3 +197,10 @@ create table room_assignments (
197
197
  room_id int not null
198
198
  );
199
199
 
200
+ create table seats (
201
+ flight_number int not null,
202
+ seat int not null,
203
+ customer int,
204
+ primary key (flight_number, seat)
205
+ );
206
+
@@ -158,3 +158,9 @@ create table room_assignments (
158
158
  room_id integer not null
159
159
  );
160
160
 
161
+ create table seats (
162
+ flight_number integer not_null,
163
+ seat integer not_null,
164
+ customer integer,
165
+ primary key (flight_number, seat)
166
+ );
@@ -4,4 +4,7 @@ class Membership < ActiveRecord::Base
4
4
  belongs_to :user
5
5
  belongs_to :group
6
6
  has_many :statuses, :class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
7
+
8
+ has_many :readings, :primary_key => :user_id, :foreign_key => :user_id
9
+ has_one :reading, :primary_key => :user_id, :foreign_key => :user_id, :order => 'id DESC'
7
10
  end
@@ -17,7 +17,8 @@ require 'fixtures/reading'
17
17
 
18
18
  class TestAssociations < ActiveSupport::TestCase
19
19
  fixtures :articles, :products, :tariffs, :product_tariffs, :suburbs, :streets, :restaurants, :restaurants_suburbs,
20
- :dorms, :rooms, :room_attributes, :room_attribute_assignments, :students, :room_assignments, :users, :readings
20
+ :dorms, :rooms, :room_attributes, :room_attribute_assignments, :students, :room_assignments, :users, :readings,
21
+ :memberships
21
22
 
22
23
  def test_has_many_through_with_conditions_when_through_association_is_not_composite
23
24
  user = User.find(:first)
@@ -157,4 +158,28 @@ class TestAssociations < ActiveSupport::TestCase
157
158
  @restaurant = Restaurant.find([1,1], :include => :suburbs)
158
159
  assert_equal 2, @restaurant.suburbs.size
159
160
  end
161
+
162
+ def test_has_many_with_primary_key
163
+ @membership = Membership.find([1, 1])
164
+
165
+ assert_equal 2, @membership.readings.size
166
+ end
167
+
168
+ def test_has_one_with_primary_key
169
+ @membership = Membership.find([1, 1])
170
+
171
+ assert_equal 2, @membership.reading.id
172
+ end
173
+
174
+ def test_joins_has_many_with_primary_key
175
+ @membership = Membership.find(:first, :joins => :readings, :conditions => { :readings => { :id => 1 } })
176
+
177
+ assert_equal [1, 1], @membership.id
178
+ end
179
+
180
+ def test_joins_has_one_with_primary_key
181
+ @membership = Membership.find(:first, :joins => :reading, :conditions => { :readings => { :id => 2 } })
182
+
183
+ assert_equal [1, 1], @membership.id
184
+ end
160
185
  end
@@ -0,0 +1,11 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/seat'
3
+
4
+ class TestValidations < ActiveSupport::TestCase
5
+ fixtures :seats
6
+
7
+ def test_uniqueness_validation_on_saved_record
8
+ s = Seat.find([1,1])
9
+ assert s.valid?
10
+ end
11
+ end
Binary file
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: 2.3.2
4
+ version: 2.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dr Nic Williams
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-16 00:00:00 -05:00
12
+ date: 2009-12-16 00:00:00 -06:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 2.3.2
23
+ version: 2.3.5
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: hoe
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 2.3.2
33
+ version: 2.4.0
34
34
  version:
35
35
  description: Composite key support for ActiveRecords
36
36
  email: drnicwilliams@gmail.com
@@ -73,6 +73,7 @@ files:
73
73
  - lib/composite_primary_keys/fixtures.rb
74
74
  - lib/composite_primary_keys/migration.rb
75
75
  - lib/composite_primary_keys/reflection.rb
76
+ - lib/composite_primary_keys/validations/uniqueness.rb
76
77
  - lib/composite_primary_keys/version.rb
77
78
  - loader.rb
78
79
  - local/database_connections.rb.sample
@@ -194,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
195
  requirements: []
195
196
 
196
197
  rubyforge_project: compositekeys
197
- rubygems_version: 1.3.4
198
+ rubygems_version: 1.3.5
198
199
  signing_key:
199
200
  specification_version: 3
200
201
  summary: Composite key support for ActiveRecords
@@ -216,3 +217,4 @@ test_files:
216
217
  - test/test_santiago.rb
217
218
  - test/test_tutorial_examle.rb
218
219
  - test/test_update.rb
220
+ - test/test_validations.rb