composite_primary_keys 3.1.11 → 4.0.0.beta1

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 (62) hide show
  1. data/History.txt +6 -8
  2. data/lib/composite_primary_keys.rb +53 -36
  3. data/lib/composite_primary_keys/associations/association.rb +23 -0
  4. data/lib/composite_primary_keys/associations/association_scope.rb +67 -0
  5. data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +31 -121
  6. data/lib/composite_primary_keys/associations/has_many_association.rb +27 -66
  7. data/lib/composite_primary_keys/associations/join_dependency/join_association.rb +22 -0
  8. data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +39 -0
  9. data/lib/composite_primary_keys/associations/preloader/association.rb +61 -0
  10. data/lib/composite_primary_keys/associations/preloader/belongs_to.rb +13 -0
  11. data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +46 -0
  12. data/lib/composite_primary_keys/attribute_methods/dirty.rb +30 -0
  13. data/lib/composite_primary_keys/attribute_methods/read.rb +88 -0
  14. data/lib/composite_primary_keys/attribute_methods/write.rb +33 -0
  15. data/lib/composite_primary_keys/base.rb +18 -70
  16. data/lib/composite_primary_keys/composite_predicates.rb +53 -0
  17. data/lib/composite_primary_keys/connection_adapters/abstract_adapter.rb +6 -4
  18. data/lib/composite_primary_keys/connection_adapters/postgresql_adapter.rb +19 -41
  19. data/lib/composite_primary_keys/fixtures.rb +19 -6
  20. data/lib/composite_primary_keys/persistence.rb +32 -13
  21. data/lib/composite_primary_keys/relation.rb +23 -16
  22. data/lib/composite_primary_keys/relation/calculations.rb +48 -0
  23. data/lib/composite_primary_keys/relation/finder_methods.rb +117 -0
  24. data/lib/composite_primary_keys/relation/query_methods.rb +24 -0
  25. data/lib/composite_primary_keys/validations/uniqueness.rb +19 -23
  26. data/lib/composite_primary_keys/version.rb +5 -5
  27. data/test/connections/native_mysql/connection.rb +1 -1
  28. data/test/fixtures/articles.yml +1 -0
  29. data/test/fixtures/products.yml +2 -4
  30. data/test/fixtures/readings.yml +1 -0
  31. data/test/fixtures/suburbs.yml +1 -4
  32. data/test/fixtures/users.yml +1 -0
  33. data/test/test_associations.rb +61 -63
  34. data/test/test_attributes.rb +16 -21
  35. data/test/test_create.rb +3 -3
  36. data/test/test_delete.rb +87 -84
  37. data/test/{test_clone.rb → test_dup.rb} +8 -5
  38. data/test/test_exists.rb +22 -10
  39. data/test/test_habtm.rb +0 -74
  40. data/test/test_ids.rb +2 -1
  41. data/test/test_miscellaneous.rb +2 -2
  42. data/test/test_polymorphic.rb +1 -1
  43. data/test/test_suite.rb +1 -1
  44. data/test/test_update.rb +3 -3
  45. metadata +76 -75
  46. data/lib/composite_primary_keys/association_preload.rb +0 -158
  47. data/lib/composite_primary_keys/associations.rb +0 -155
  48. data/lib/composite_primary_keys/associations/association_proxy.rb +0 -33
  49. data/lib/composite_primary_keys/associations/has_one_association.rb +0 -27
  50. data/lib/composite_primary_keys/associations/through_association_scope.rb +0 -103
  51. data/lib/composite_primary_keys/attribute_methods.rb +0 -84
  52. data/lib/composite_primary_keys/calculations.rb +0 -31
  53. data/lib/composite_primary_keys/connection_adapters/ibm_db_adapter.rb +0 -21
  54. data/lib/composite_primary_keys/connection_adapters/oracle_adapter.rb +0 -15
  55. data/lib/composite_primary_keys/connection_adapters/oracle_enhanced_adapter.rb +0 -17
  56. data/lib/composite_primary_keys/connection_adapters/sqlite3_adapter.rb +0 -15
  57. data/lib/composite_primary_keys/finder_methods.rb +0 -123
  58. data/lib/composite_primary_keys/primary_key.rb +0 -19
  59. data/lib/composite_primary_keys/query_methods.rb +0 -24
  60. data/lib/composite_primary_keys/read.rb +0 -25
  61. data/lib/composite_primary_keys/reflection.rb +0 -37
  62. data/lib/composite_primary_keys/write.rb +0 -18
@@ -1,84 +0,0 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module AttributeMethods #:nodoc:
4
- def self.append_features(base)
5
- super
6
- base.send(:extend, ClassMethods)
7
- end
8
-
9
- module ClassMethods
10
- # Define an attribute reader method. Cope with nil column.
11
- def define_read_method(symbol, attr_name, column)
12
- cast_code = column.type_cast_code('v') if column
13
- cast_code = "::#{cast_code}" if cast_code && cast_code.match('ActiveRecord::.*')
14
- access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
15
-
16
- unless self.primary_keys.include?(attr_name.to_sym)
17
- access_code = access_code.insert(0, "missing_attribute('#{attr_name}', caller) unless @attributes.has_key?('#{attr_name}'); ")
18
- end
19
-
20
- if cache_attribute?(attr_name)
21
- access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
22
- end
23
-
24
- evaluate_attribute_method attr_name, "def #{symbol}; #{access_code}; end"
25
- end
26
-
27
- # Evaluate the definition for an attribute related method
28
- def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
29
- unless primary_keys.include?(method_name.to_sym)
30
- generated_methods << method_name
31
- end
32
-
33
- begin
34
- class_eval(method_definition, __FILE__, __LINE__)
35
- rescue SyntaxError => err
36
- generated_methods.delete(attr_name)
37
- if logger
38
- logger.warn "Exception occurred during reader method compilation."
39
- logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
40
- logger.warn "#{err.message}"
41
- end
42
- end
43
- end
44
- end
45
-
46
- # Allows access to the object attributes, which are held in the @attributes hash, as though they
47
- # were first-class methods. So a Person class with a name attribute can use Person#name and
48
- # Person#name= and never directly use the attributes hash -- except for multiple assigns with
49
- # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that
50
- # the completed attribute is not nil or 0.
51
- #
52
- # It's also possible to instantiate related objects, so a Client class belonging to the clients
53
- # table with a master_id foreign key can instantiate master through Client#master.
54
- def method_missing(method_id, *args, &block)
55
- method_name = method_id.to_s
56
-
57
- # If we haven't generated any methods yet, generate them, then
58
- # see if we've created the method we're looking for.
59
- if !self.class.generated_methods?
60
- self.class.define_attribute_methods
61
-
62
- if self.class.generated_methods.include?(method_name)
63
- return self.send(method_id, *args, &block)
64
- end
65
- end
66
-
67
- if self.class.primary_keys.include?(method_name.to_sym)
68
- ids[self.class.primary_keys.index(method_name.to_sym)]
69
- elsif md = self.class.match_attribute_method?(method_name)
70
- attribute_name, method_type = md.pre_match, md.to_s
71
- if @attributes.include?(attribute_name)
72
- __send__("attribute#{method_type}", attribute_name, *args, &block)
73
- else
74
- super
75
- end
76
- elsif @attributes.include?(method_name)
77
- read_attribute(method_name)
78
- else
79
- super
80
- end
81
- end
82
- end
83
- end
84
- end
@@ -1,31 +0,0 @@
1
- module ActiveRecord
2
- module Calculations
3
- alias :execute_simple_calculation_ar :execute_simple_calculation
4
- def execute_simple_calculation(operation, column_name, distinct)
5
- # CPK
6
- if column_name.kind_of?(Array)
7
- execute_simple_calculation_cpk(operation, column_name, distinct)
8
- else
9
- execute_simple_calculation_ar(operation, column_name, distinct)
10
- end
11
- end
12
-
13
- def execute_simple_calculation_cpk(operation, column_name, distinct)
14
- projection = self.primary_keys.map do |key|
15
- attribute = arel_table[key]
16
- self.arel.visitor.accept(attribute)
17
- end.join(', ')
18
-
19
- # relation = self.clone
20
- # relation.select_values = ["DISTINCT #{projection}"]
21
-
22
- manager = Arel::SelectManager.new(arel_engine)
23
- manager.project("DISTINCT #{projection}")
24
- manager.from(arel_table)
25
-
26
- query = Arel::Table.new('dummy').project('count(*)')
27
- query = query.from("(#{manager.to_sql}) AS subquery")
28
- type_cast_calculated_value(@klass.connection.select_value(query.to_sql), column_for(column_name), operation)
29
- end
30
- end
31
- end
@@ -1,21 +0,0 @@
1
- module ActiveRecord
2
- module ConnectionAdapters
3
- class IBM_DBAdapter < AbstractAdapter
4
-
5
- # This mightn't be in Core, but count(distinct x,y) doesn't work for me
6
- def supports_count_distinct? #:nodoc:
7
- false
8
- end
9
-
10
- alias_method :quote_original, :quote
11
- def quote(value, column = nil)
12
- if value.kind_of?(String) && column && [:integer, :float].include?(column.type)
13
- value = column.type == :integer ? value.to_i : value.to_f
14
- value.to_s
15
- else
16
- quote_original(value, column)
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,15 +0,0 @@
1
- module ActiveRecord
2
- module ConnectionAdapters
3
- class OracleAdapter < AbstractAdapter
4
-
5
- # This mightn't be in Core, but count(distinct x,y) doesn't work for me
6
- def supports_count_distinct? #:nodoc:
7
- false
8
- end
9
-
10
- def concat(*columns)
11
- "(#{columns.join('||')})"
12
- end
13
- end
14
- end
15
- end
@@ -1,17 +0,0 @@
1
- # Added to OracleEnhancedAdapter version 1.1.4
2
- #
3
- # module ActiveRecord
4
- # module ConnectionAdapters
5
- # class OracleEnhancedAdapter < AbstractAdapter
6
- #
7
- # # This mightn't be in Core, but count(distinct x,y) doesn't work for me
8
- # def supports_count_distinct? #:nodoc:
9
- # false
10
- # end
11
- #
12
- # def concat(*columns)
13
- # "(#{columns.join('||')})"
14
- # end
15
- # end
16
- # end
17
- # end
@@ -1,15 +0,0 @@
1
- require 'active_record/connection_adapters/sqlite3_adapter'
2
-
3
- module ActiveRecord
4
- module ConnectionAdapters #:nodoc:
5
- class SQLite3Adapter # :nodoc:
6
- def supports_count_distinct? #:nodoc:
7
- false
8
- end
9
-
10
- def concat(*columns)
11
- "(#{columns.join('||')})"
12
- end
13
- end
14
- end
15
- end
@@ -1,123 +0,0 @@
1
- module CompositePrimaryKeys
2
- module ActiveRecord
3
- module FinderMethods
4
- module InstanceMethods
5
- def construct_limited_ids_condition(relation)
6
- orders = relation.order_values.join(", ")
7
-
8
- # CPK
9
- # values = @klass.connection.distinct("#{@klass.connection.quote_table_name @klass.table_name}.#{@klass.primary_key}", orders)
10
- keys = @klass.primary_keys.map do |key|
11
- "#{@klass.connection.quote_table_name @klass.table_name}.#{key}"
12
- end
13
-
14
- values = @klass.connection.distinct(keys.join(', '), orders)
15
-
16
- ids_array = relation.select(values).collect {|row| row[@klass.primary_key]}
17
-
18
- # CPK
19
- # ids_array.empty? ? raise(ThrowResult) : primary_key.in(ids_array)
20
-
21
- # OR together each and expression (key=value and key=value) that matches an id set
22
- # since we only need to match 0 or more records
23
- or_expressions = ids_array.map do |id_set|
24
- # AND together "key=value" exprssios to match each id set
25
- and_expressions = [self.primary_keys, id_set].transpose.map do |key, id|
26
- table[key].eq(id)
27
- end
28
-
29
- # Merge all the ands together
30
- first = and_expressions.shift
31
- Arel::Nodes::Grouping.new(and_expressions.inject(first) do |memo, expr|
32
- Arel::Nodes::And.new(memo, expr)
33
- end)
34
- end
35
-
36
- first = or_expressions.shift
37
- Arel::Nodes::Grouping.new(or_expressions.inject(first) do |memo, expr|
38
- Arel::Nodes::Or.new(memo, expr)
39
- end)
40
- end
41
-
42
- def exists?(id = nil)
43
- # ID can be:
44
- # Array - ['department_id = ? and location_id = ?', 1, 1]
45
- # Array -> [1,2]
46
- # CompositeKeys -> [1,2]
47
-
48
- id = id.id if ::ActiveRecord::Base === id
49
-
50
- case id
51
- # CPK
52
- when CompositePrimaryKeys::CompositeKeys
53
- relation = select("1").limit(1)
54
- relation = relation.where_cpk_id(id) if id
55
- relation.first ? true : false
56
- when Array
57
- # CPK
58
- if id.first.is_a?(String) and id.first.match(/\?/)
59
- where(id).exists?
60
- else
61
- exists?(id.to_composite_keys)
62
- end
63
- when Hash
64
- where(id).exists?
65
- else
66
- # CPK
67
- #relation = select(primary_key).limit(1)
68
- #relation = relation.where(primary_key.eq(id)) if id
69
- relation = select("1").limit(1)
70
- relation = relation.where_cpk_id(id) if id
71
- relation.first ? true : false
72
- end
73
- end
74
-
75
- def find_with_ids(*ids, &block)
76
- return to_a.find { |*block_args| yield(*block_args) } if block_given?
77
-
78
- # Supports:
79
- # find('1,2') -> ['1,2']
80
- # find(1,2) -> [1,2]
81
- # find([1,2]) -> [['1,2']]
82
- # find([1,2], [3,4]) -> [[1,2],[3,4]]
83
- #
84
- # Does *not* support:
85
- # find('1,2', '3,4') -> ['1,2','3,4']
86
-
87
- # Normalize incoming data. Note the last arg can be nil. Happens
88
- # when find is called with nil options like the reload method does.
89
- ids.compact!
90
- ids = [ids] unless ids.first.kind_of?(Array)
91
-
92
- results = ids.map do |cpk_ids|
93
- cpk_ids = if cpk_ids.length == 1
94
- cpk_ids.first.split(CompositePrimaryKeys::ID_SEP).to_composite_keys
95
- else
96
- cpk_ids.to_composite_keys
97
- end
98
-
99
- unless cpk_ids.length == @klass.primary_keys.length
100
- raise "#{cpk_ids.inspect}: Incorrect number of primary keys for #{@klass.name}: #{@klass.primary_keys.inspect}"
101
- end
102
-
103
- new_relation = clone
104
- [@klass.primary_keys, cpk_ids].transpose.map do |key, id|
105
- new_relation = new_relation.where(key => id)
106
- end
107
-
108
- records = new_relation.to_a
109
-
110
- if records.empty?
111
- conditions = new_relation.arel.where_sql
112
- raise(::ActiveRecord::RecordNotFound,
113
- "Couldn't find #{@klass.name} with ID=#{cpk_ids} #{conditions}")
114
- end
115
- records
116
- end.flatten
117
-
118
- ids.length == 1 ? results.first : results
119
- end
120
- end
121
- end
122
- end
123
- 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,24 +0,0 @@
1
- module ActiveRecord
2
- module QueryMethods
3
- def reverse_order
4
- order_clause = arel.order_clauses.join(', ')
5
- relation = except(:order)
6
-
7
- # CPK
8
- # order = order_clause.blank? ?
9
- # "#{@klass.table_name}.#{@klass.primary_key} DESC" :
10
- # reverse_sql_order(order_clause)
11
-
12
- order = unless order_clause.blank?
13
- reverse_sql_order(order_clause)
14
- else
15
- primary_keys = composite? ? @klass.primary_keys : [@klass.primary_key]
16
- primary_keys.map do |key|
17
- "#{@klass.table_name}.#{key} DESC"
18
- end.join(", ")
19
- end
20
-
21
- relation.order Arel::SqlLiteral.new order
22
- end
23
- end
24
- end
@@ -1,25 +0,0 @@
1
- module ActiveRecord
2
- module AttributeMethods
3
- module Read
4
- def read_attribute(attr_name)
5
- attr_name = attr_name.to_s
6
- # CPK
7
- # attr_name = self.class.primary_key if attr_name == 'id'
8
- attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
9
- if !(value = @attributes[attr_name]).nil?
10
- if column = column_for_attribute(attr_name)
11
- if unserializable_attribute?(attr_name, column)
12
- unserialize_attribute(attr_name)
13
- else
14
- column.type_cast(value)
15
- end
16
- else
17
- value
18
- end
19
- else
20
- nil
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,37 +0,0 @@
1
- module ActiveRecord
2
- module Reflection
3
- class AssociationReflection
4
- def derive_primary_key
5
- if options[:foreign_key]
6
- options[:foreign_key]
7
- elsif belongs_to?
8
- "#{name}_id"
9
- elsif options[:as]
10
- "#{options[:as]}_id"
11
- else
12
- active_record.name.foreign_key
13
- end
14
- end
15
-
16
- def cpk_primary_key
17
- # Make sure the returned key(s) are an array
18
- @cpk_primary_key ||= [derive_primary_key].flatten
19
- end
20
-
21
- def primary_key_name
22
- @primary_key_name ||= derive_primary_key_name
23
- end
24
-
25
- def derive_primary_key_name
26
- result = derive_primary_key
27
-
28
- # CPK
29
- if result.is_a?(Array)
30
- result.to_composite_keys.to_s
31
- else
32
- result
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,18 +0,0 @@
1
- module ActiveRecord
2
- module AttributeMethods
3
- module Write
4
- def write_attribute(attr_name, value)
5
- attr_name = attr_name.to_s
6
- # CPK
7
- # attr_name = self.class.primary_key if attr_name == 'id'
8
- attr_name = self.class.primary_key if (attr_name == 'id' and !self.composite?)
9
- @attributes_cache.delete(attr_name)
10
- if (column = column_for_attribute(attr_name)) && column.number?
11
- @attributes[attr_name] = convert_number_column_value(value)
12
- else
13
- @attributes[attr_name] = value
14
- end
15
- end
16
- end
17
- end
18
- end