composite_primary_keys 3.1.8 → 3.1.9

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.
Files changed (52) hide show
  1. data/History.txt +16 -12
  2. data/Rakefile +2 -4
  3. data/lib/composite_primary_keys/version.rb +1 -1
  4. data/test/connections/native_mysql/connection.rb +1 -1
  5. data/test/connections/native_oracle/connection.rb +1 -1
  6. data/test/connections/native_oracle_enhanced/connection.rb +1 -1
  7. data/test/connections/native_postgresql/connection.rb +1 -1
  8. data/test/connections/native_sqlite/connection.rb +1 -1
  9. data/test/fixtures/article.rb +1 -1
  10. data/test/fixtures/comment.rb +0 -1
  11. data/test/fixtures/db_definitions/db2-create-tables.sql +4 -13
  12. data/test/fixtures/db_definitions/db2-drop-tables.sql +1 -1
  13. data/test/fixtures/db_definitions/mysql.sql +5 -13
  14. data/test/fixtures/db_definitions/oracle.drop.sql +1 -2
  15. data/test/fixtures/db_definitions/oracle.sql +4 -13
  16. data/test/fixtures/db_definitions/postgresql.sql +5 -13
  17. data/test/fixtures/db_definitions/sqlite.sql +4 -13
  18. data/test/fixtures/department.rb +1 -0
  19. data/test/fixtures/departments.yml +5 -1
  20. data/test/fixtures/employees.yml +12 -2
  21. data/test/fixtures/membership.rb +0 -2
  22. data/test/fixtures/product.rb +4 -2
  23. data/test/fixtures/restaurant.rb +4 -1
  24. data/test/fixtures/restaurants.yml +8 -1
  25. data/test/fixtures/tariff.rb +0 -1
  26. data/test/fixtures/tariffs.yml +2 -0
  27. data/test/test_associations.rb +6 -23
  28. data/test/test_attribute_methods.rb +5 -6
  29. data/test/test_delete.rb +35 -2
  30. data/test/test_habtm.rb +42 -0
  31. data/test/test_ids.rb +0 -8
  32. data/test/test_suite.rb +1 -0
  33. metadata +6 -23
  34. data/lib/composite_primary_keys/associations/association.rb +0 -23
  35. data/lib/composite_primary_keys/associations/association_scope.rb +0 -67
  36. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +0 -22
  37. data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +0 -39
  38. data/lib/composite_primary_keys/associations/preloader/association.rb +0 -61
  39. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +0 -13
  40. data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +0 -46
  41. data/lib/composite_primary_keys/attribute_methods/primary_key.rb +0 -19
  42. data/lib/composite_primary_keys/attribute_methods/read.rb +0 -88
  43. data/lib/composite_primary_keys/attribute_methods/write.rb +0 -40
  44. data/lib/composite_primary_keys/composite_predicates.rb +0 -44
  45. data/lib/composite_primary_keys/named_scope.rb +0 -29
  46. data/lib/composite_primary_keys/relation/finder_methods.rb +0 -119
  47. data/lib/composite_primary_keys/relation/query_methods.rb +0 -24
  48. data/test/fixtures/article_group.rb +0 -4
  49. data/test/fixtures/article_groups.yml +0 -7
  50. data/test/fixtures/kitchen_sink.rb +0 -3
  51. data/test/fixtures/kitchen_sinks.yml +0 -5
  52. data/test/fixtures/way_node.rb +0 -3
@@ -1,13 +0,0 @@
1
- module ActiveRecord
2
- module Associations
3
- class Preloader
4
- class BelongsTo
5
- def records_for(ids)
6
- # CPK
7
- predicate = cpk_in_predicate(table, association_key_name, ids)
8
- scoped.where(predicate)
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,46 +0,0 @@
1
- module ActiveRecord
2
- module Associations
3
- class Preloader
4
- class HasAndBelongsToMany
5
- def records_for(ids)
6
- # CPK
7
- #scope = super
8
- predicate = cpk_in_predicate(join_table, reflection.foreign_key, ids)
9
- scope = scoped.where(predicate)
10
-
11
- klass.connection.select_all(scope.arel.to_sql, 'SQL', scope.bind_values)
12
- end
13
-
14
- def join
15
- # CPK
16
- #condition = table[reflection.association_primary_key].eq(
17
- # join_table[reflection.association_foreign_key])
18
- condition = cpk_join_predicate(table, reflection.association_primary_key,
19
- join_table, reflection.association_foreign_key)
20
-
21
- table.create_join(join_table, table.create_on(condition))
22
- end
23
-
24
- def association_key_alias(field)
25
- "ar_association_key_name_#{field.to_s}"
26
- end
27
-
28
- def join_select
29
- # CPK
30
- # association_key.as(Arel.sql(association_key_name))
31
- Array(reflection.foreign_key).map do |key|
32
- join_table[key].as(Arel.sql(association_key_alias(key)))
33
- end
34
- end
35
-
36
- def association_key_name
37
- # CPK
38
- # 'ar_association_key_name'
39
- Array(reflection.foreign_key).map do |key|
40
- association_key_alias(key)
41
- end
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,19 +0,0 @@
1
- module ActiveRecord
2
- module AttributeMethods #:nodoc:
3
- module PrimaryKey
4
- def to_key
5
- # CPK
6
- #key = send(self.class.primary_key)
7
- #[key] if key
8
-
9
- primary_key = self.class.primary_key
10
- if primary_key.is_a?(Array)
11
- primary_key.collect{|k| send(k)}
12
- else
13
- key = send(primary_key)
14
- [key] if key
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,88 +0,0 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module AttributeMethods
4
- module Read
5
- def self.included(base)
6
- base.send(:extend, ClassMethods)
7
- alias [] read_attribute
8
- end
9
-
10
- module ClassMethods
11
- def define_read_method(method_name, attr_name, column)
12
- cast_code = column.type_cast_code('v')
13
- # CPK - this is a really horrid hack, needed to get
14
- # right class namespace :(
15
- if cast_code.match(/^ActiveRecord/)
16
- cast_code = "::#{cast_code}"
17
- end
18
-
19
- access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}"
20
-
21
- # CPK
22
- # unless attr_name.to_s == self.primary_key.to_s
23
- # access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
24
- # end
25
- unless self.primary_keys.include?(attr_name.to_sym)
26
- access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
27
- end
28
-
29
- if cache_attribute?(attr_name)
30
- access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
31
- end
32
-
33
- # Where possible, generate the method by evalling a string, as this will result in
34
- # faster accesses because it avoids the block eval and then string eval incurred
35
- # by the second branch.
36
- #
37
- # The second, slower, branch is necessary to support instances where the database
38
- # returns columns with extra stuff in (like 'my_column(omg)').
39
- if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
40
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
41
- def _#{method_name}
42
- #{access_code}
43
- end
44
-
45
- alias #{method_name} _#{method_name}
46
- STR
47
- else
48
- generated_attribute_methods.module_eval do
49
- define_method("_#{method_name}") { eval(access_code) }
50
- alias_method(method_name, "_#{method_name}")
51
- end
52
- end
53
- end
54
- end
55
-
56
- def read_attribute(attr_name)
57
- # CPK
58
- if attr_name.kind_of?(Array)
59
- attr_name.map {|name| read_attribute(name)}.to_composite_keys
60
- elsif respond_to? "_#{attr_name}"
61
- send "_#{attr_name}" if @attributes.has_key?(attr_name.to_s)
62
- else
63
- _read_attribute attr_name
64
- end
65
- end
66
-
67
- def _read_attribute(attr_name)
68
- attr_name = attr_name.to_s
69
- # CPK
70
- # attr_name = self.class.primary_key if attr_name == 'id'
71
- attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
72
- value = @attributes[attr_name]
73
- unless value.nil?
74
- if column = column_for_attribute(attr_name)
75
- if unserializable_attribute?(attr_name, column)
76
- unserialize_attribute(attr_name)
77
- else
78
- column.type_cast(value)
79
- end
80
- else
81
- value
82
- end
83
- end
84
- end
85
- end
86
- end
87
- end
88
- end
@@ -1,40 +0,0 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module AttributeMethods
4
- module Write
5
- def self.included(base)
6
- alias []= write_attribute
7
- alias_method :raw_write_attribute, :write_attribute
8
- end
9
-
10
- def write_attribute(attr_name, value)
11
- if attr_name.kind_of?(Array)
12
- unless value.length == attr_name.length
13
- raise "Number of attr_names and values do not match"
14
- end
15
- [attr_name, value].transpose.map {|name,val| _write_attribute(name, val)}
16
- value
17
- else
18
- _write_attribute(attr_name, value)
19
- end
20
- end
21
-
22
- def _write_attribute(attr_name, value)
23
- attr_name = attr_name.to_s
24
- # CPK
25
- # attr_name = self.class.primary_key if attr_name == 'id'
26
- attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
27
- @attributes_cache.delete(attr_name)
28
- if (column = column_for_attribute(attr_name)) && column.number?
29
- @attributes[attr_name] = convert_number_column_value(value)
30
- else
31
- @attributes[attr_name] = value
32
- end
33
- end
34
-
35
-
36
-
37
- end
38
- end
39
- end
40
- end
@@ -1,44 +0,0 @@
1
- module CompositePrimaryKeys
2
- module Predicates
3
- def cpk_or_predicate(predicates)
4
- result = predicates.shift
5
- predicates.each do |predicate|
6
- result = result.or(predicate)
7
- end
8
- result
9
- end
10
-
11
- def cpk_id_predicate(table, keys, values)
12
- eq_predicates = keys.zip(values).map do |key, value|
13
- table[key].eq(value)
14
- end
15
- Arel::Nodes::And.new(eq_predicates)
16
- end
17
-
18
- def cpk_join_predicate(table1, key1, table2, key2)
19
- key1_fields = Array(key1).map {|key| table1[key]}
20
- key2_fields = Array(key2).map {|key| table2[key]}
21
-
22
- predicates = key1_fields.zip(key2_fields).map do |key_field1, key_field2|
23
- key_field1.eq(key_field2)
24
- end
25
- Arel::Nodes::And.new(predicates)
26
- end
27
-
28
- def cpk_in_predicate(table, primary_keys, ids)
29
- and_predicates = ids.map do |id_set|
30
- eq_predicates = Array(primary_keys).zip(id_set).map do |primary_key, value|
31
- table[primary_key].eq(value)
32
- end
33
- Arel::Nodes::And.new(eq_predicates)
34
- end
35
-
36
- cpk_or_predicate(and_predicates)
37
- end
38
- end
39
- end
40
-
41
- ActiveRecord::Associations::AssociationScope.send(:include, CompositePrimaryKeys::Predicates)
42
- ActiveRecord::Associations::JoinDependency::JoinAssociation.send(:include, CompositePrimaryKeys::Predicates)
43
- ActiveRecord::Associations::Preloader::Association.send(:include, CompositePrimaryKeys::Predicates)
44
- ActiveRecord::Relation.send(:include, CompositePrimaryKeys::Predicates)
@@ -1,29 +0,0 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module NamedScope
4
- module ClassMethods
5
- def scoped(options = nil)
6
- result = if options
7
- scoped.apply_finder_options(options)
8
- else
9
- if current_scope
10
- current_scope.clone
11
- else
12
- scope = relation.clone
13
- scope.default_scoped = true
14
- scope
15
- end
16
- end
17
-
18
- # CPK
19
- class << result
20
- include CompositePrimaryKeys::ActiveRecord::FinderMethods
21
- include CompositePrimaryKeys::ActiveRecord::QueryMethods
22
- include CompositePrimaryKeys::ActiveRecord::Relation
23
- end
24
- result
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,119 +0,0 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module FinderMethods
4
- def construct_limited_ids_condition(relation)
5
- orders = relation.order_values
6
- # CPK
7
- # values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
8
- keys = @klass.primary_keys.map do |key|
9
- "#{@klass.connection.quote_table_name @klass.table_name}.#{key}"
10
- end
11
- values = @klass.connection.distinct(keys.join(', '), orders)
12
-
13
- relation = relation.dup
14
-
15
- ids_array = relation.select(values).collect {|row| row[primary_key]}
16
- # CPK
17
- # ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
18
-
19
- # OR together each and expression (key=value and key=value) that matches an id set
20
- # since we only need to match 0 or more records
21
- or_expressions = ids_array.map do |id_set|
22
- # AND together "key=value" exprssios to match each id set
23
- and_expressions = [self.primary_keys, id_set].transpose.map do |key, id|
24
- table[key].eq(id)
25
- end
26
- Arel::Nodes::And.new(and_expressions)
27
- end
28
-
29
- first = or_expressions.shift
30
- Arel::Nodes::Grouping.new(or_expressions.inject(first) do |memo, expr|
31
- Arel::Nodes::Or.new(memo, expr)
32
- end)
33
- end
34
-
35
- def exists?(id = nil)
36
- # ID can be:
37
- # Array - ['department_id = ? and location_id = ?', 1, 1]
38
- # Array -> [1,2]
39
- # CompositeKeys -> [1,2]
40
-
41
- id = id.id if ::ActiveRecord::Base === id
42
-
43
- join_dependency = construct_join_dependency_for_association_find
44
- relation = construct_relation_for_association_find(join_dependency)
45
- relation = relation.except(:select).select("1").limit(1)
46
-
47
- # CPK
48
- #case id
49
- #when Array, Hash
50
- # relation = relation.where(id)
51
- #else
52
- # relation = relation.where(table[primary_key].eq(id)) if id
53
- #end
54
-
55
- case id
56
- when CompositePrimaryKeys::CompositeKeys
57
- relation = relation.where(cpk_id_predicate(table, primary_key, id))
58
- when Array
59
- if !id.first.kind_of?(String)
60
- return self.exists?(id.to_composite_keys)
61
- else
62
- relation = relation.where(id)
63
- end
64
- when Hash
65
- relation = relation.where(id)
66
- else
67
- raise(ArgumentError, "Unsupported id sent to exists?")
68
- end
69
- connection.select_value(relation.to_sql) ? true : false
70
- end
71
-
72
- def find_with_ids(*ids, &block)
73
- return to_a.find { |*block_args| yield(*block_args) } if block_given?
74
-
75
- # Supports:
76
- # find('1,2') -> ['1,2']
77
- # find(1,2) -> [1,2]
78
- # find([1,2]) -> [['1,2']]
79
- # find([1,2], [3,4]) -> [[1,2],[3,4]]
80
- #
81
- # Does *not* support:
82
- # find('1,2', '3,4') -> ['1,2','3,4']
83
-
84
- # Normalize incoming data. Note the last arg can be nil. Happens
85
- # when find is called with nil options like the reload method does.
86
- ids.compact!
87
- ids = [ids] unless ids.first.kind_of?(Array)
88
-
89
- results = ids.map do |cpk_ids|
90
- cpk_ids = if cpk_ids.length == 1
91
- cpk_ids.first.split(CompositePrimaryKeys::ID_SEP).to_composite_keys
92
- else
93
- cpk_ids.to_composite_keys
94
- end
95
-
96
- unless cpk_ids.length == @klass.primary_keys.length
97
- raise "#{cpk_ids.inspect}: Incorrect number of primary keys for #{@klass.name}: #{@klass.primary_keys.inspect}"
98
- end
99
-
100
- new_relation = clone
101
- [@klass.primary_keys, cpk_ids].transpose.map do |key, id|
102
- new_relation = new_relation.where(key => id)
103
- end
104
-
105
- records = new_relation.to_a
106
-
107
- if records.empty?
108
- conditions = new_relation.arel.where_sql
109
- raise(::ActiveRecord::RecordNotFound,
110
- "Couldn't find #{@klass.name} with ID=#{cpk_ids} #{conditions}")
111
- end
112
- records
113
- end.flatten
114
-
115
- ids.length == 1 ? results.first : results
116
- end
117
- end
118
- end
119
- end
@@ -1,24 +0,0 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module QueryMethods
4
- def reverse_order
5
- order_clause = arel.order_clauses
6
-
7
- # CPK
8
- # order = order_clause.empty? ?
9
- # "#{table_name}.#{primary_key} DESC" :
10
- # reverse_sql_order(order_clause).join(', ')
11
-
12
- order = unless order_clause.empty?
13
- reverse_sql_order(order_clause).join(', ')
14
- else
15
- klass.primary_key.map do |key|
16
- "#{table_name}.#{key} DESC"
17
- end.join(", ")
18
- end
19
-
20
- except(:order).order(Arel.sql(order))
21
- end
22
- end
23
- end
24
- end
@@ -1,4 +0,0 @@
1
- class ArticleGroup < ActiveRecord::Base
2
- belongs_to :article
3
- belongs_to :group
4
- end
@@ -1,7 +0,0 @@
1
- cpk:
2
- article_id: 1
3
- group_id: 1
4
-
5
- transformers:
6
- article_id: 1
7
- group_id: 2