square-activerecord 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +359 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,40 @@
1
+ require "active_record/associations/through_association_scope"
2
+
3
+ module ActiveRecord
4
+ # = Active Record Has One Through Association
5
+ module Associations
6
+ class HasOneThroughAssociation < HasOneAssociation
7
+ include ThroughAssociationScope
8
+
9
+ def replace(new_value)
10
+ create_through_record(new_value)
11
+ @target = new_value
12
+ end
13
+
14
+ private
15
+
16
+ def create_through_record(new_value) #nodoc:
17
+ klass = @reflection.through_reflection.klass
18
+
19
+ current_object = @owner.send(@reflection.through_reflection.name)
20
+
21
+ if current_object
22
+ new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
23
+ elsif new_value
24
+ if @owner.new_record?
25
+ self.target = new_value
26
+ through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name)
27
+ through_association.build(construct_join_attributes(new_value))
28
+ else
29
+ @owner.send(@reflection.through_reflection.name, klass.create(construct_join_attributes(new_value)))
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+ def find_target
36
+ with_scope(construct_scope) { @reflection.klass.find(:first) }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,154 @@
1
+ module ActiveRecord
2
+ # = Active Record Through Association Scope
3
+ module Associations
4
+ module ThroughAssociationScope
5
+
6
+ protected
7
+
8
+ def construct_scope
9
+ { :create => construct_owner_attributes(@reflection),
10
+ :find => { :conditions => construct_conditions,
11
+ :joins => construct_joins,
12
+ :include => @reflection.options[:include] || @reflection.source_reflection.options[:include],
13
+ :select => construct_select,
14
+ :order => @reflection.options[:order],
15
+ :limit => @reflection.options[:limit],
16
+ :readonly => @reflection.options[:readonly],
17
+ } }
18
+ end
19
+
20
+ # Build SQL conditions from attributes, qualified by table name.
21
+ def construct_conditions
22
+ table_name = @reflection.through_reflection.quoted_table_name
23
+ conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
24
+ "#{table_name}.#{attr} = #{value}"
25
+ end
26
+ conditions << sql_conditions if sql_conditions
27
+ "(" + conditions.join(') AND (') + ")"
28
+ end
29
+
30
+ # Associate attributes pointing to owner, quoted.
31
+ def construct_quoted_owner_attributes(reflection)
32
+ if as = reflection.options[:as]
33
+ { "#{as}_id" => owner_quoted_id,
34
+ "#{as}_type" => reflection.klass.quote_value(
35
+ @owner.class.base_class.name.to_s,
36
+ reflection.klass.columns_hash["#{as}_type"]) }
37
+ elsif reflection.macro == :belongs_to
38
+ { reflection.klass.primary_key => @owner.class.quote_value(@owner[reflection.primary_key_name]) }
39
+ else
40
+ { reflection.primary_key_name => owner_quoted_id }
41
+ end
42
+ end
43
+
44
+ def construct_from
45
+ @reflection.table_name
46
+ end
47
+
48
+ def construct_select(custom_select = nil)
49
+ distinct = "DISTINCT " if @reflection.options[:uniq]
50
+ selected = custom_select || @reflection.options[:select] || "#{distinct}#{@reflection.quoted_table_name}.*"
51
+ end
52
+
53
+ def construct_joins(custom_joins = nil)
54
+ polymorphic_join = nil
55
+ if @reflection.source_reflection.macro == :belongs_to
56
+ reflection_primary_key = @reflection.klass.primary_key
57
+ source_primary_key = @reflection.source_reflection.primary_key_name
58
+ if @reflection.options[:source_type]
59
+ polymorphic_join = "AND %s.%s = %s" % [
60
+ @reflection.through_reflection.quoted_table_name, "#{@reflection.source_reflection.options[:foreign_type]}",
61
+ @owner.class.quote_value(@reflection.options[:source_type])
62
+ ]
63
+ end
64
+ else
65
+ reflection_primary_key = @reflection.source_reflection.primary_key_name
66
+ source_primary_key = @reflection.through_reflection.klass.primary_key
67
+ if @reflection.source_reflection.options[:as]
68
+ polymorphic_join = "AND %s.%s = %s" % [
69
+ @reflection.quoted_table_name, "#{@reflection.source_reflection.options[:as]}_type",
70
+ @owner.class.quote_value(@reflection.through_reflection.klass.name)
71
+ ]
72
+ end
73
+ end
74
+
75
+ "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [
76
+ @reflection.through_reflection.quoted_table_name,
77
+ @reflection.quoted_table_name, reflection_primary_key,
78
+ @reflection.through_reflection.quoted_table_name, source_primary_key,
79
+ polymorphic_join
80
+ ]
81
+ end
82
+
83
+ # Construct attributes for associate pointing to owner.
84
+ def construct_owner_attributes(reflection)
85
+ if as = reflection.options[:as]
86
+ { "#{as}_id" => @owner.id,
87
+ "#{as}_type" => @owner.class.base_class.name.to_s }
88
+ else
89
+ { reflection.primary_key_name => @owner.id }
90
+ end
91
+ end
92
+
93
+ # Construct attributes for :through pointing to owner and associate.
94
+ def construct_join_attributes(associate)
95
+ # TODO: revisit this to allow it for deletion, supposing dependent option is supported
96
+ raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro)
97
+
98
+ join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id)
99
+
100
+ if @reflection.options[:source_type]
101
+ join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
102
+ end
103
+
104
+ if @reflection.through_reflection.options[:conditions].is_a?(Hash)
105
+ join_attributes.merge!(@reflection.through_reflection.options[:conditions])
106
+ end
107
+
108
+ join_attributes
109
+ end
110
+
111
+ def conditions
112
+ @conditions = build_conditions unless defined?(@conditions)
113
+ @conditions
114
+ end
115
+
116
+ def build_conditions
117
+ association_conditions = @reflection.options[:conditions]
118
+ through_conditions = build_through_conditions
119
+ source_conditions = @reflection.source_reflection.options[:conditions]
120
+ uses_sti = !@reflection.through_reflection.klass.descends_from_active_record?
121
+
122
+ if association_conditions || through_conditions || source_conditions || uses_sti
123
+ all = []
124
+
125
+ [association_conditions, source_conditions].each do |conditions|
126
+ all << interpolate_and_sanitize_sql(conditions) if conditions
127
+ end
128
+
129
+ all << through_conditions if through_conditions
130
+ all << build_sti_condition if uses_sti
131
+
132
+ all.map { |sql| "(#{sql})" } * ' AND '
133
+ end
134
+ end
135
+
136
+ def build_through_conditions
137
+ conditions = @reflection.through_reflection.options[:conditions]
138
+ if conditions.is_a?(Hash)
139
+ interpolate_and_sanitize_sql(conditions, nil, @reflection.through_reflection.klass).gsub(
140
+ @reflection.quoted_table_name,
141
+ @reflection.through_reflection.quoted_table_name)
142
+ elsif conditions
143
+ interpolate_and_sanitize_sql(conditions)
144
+ end
145
+ end
146
+
147
+ def build_sti_condition
148
+ @reflection.through_reflection.klass.send(:type_condition).to_sql
149
+ end
150
+
151
+ alias_method :sql_conditions, :conditions
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,60 @@
1
+ require 'active_support/core_ext/enumerable'
2
+
3
+ module ActiveRecord
4
+ # = Active Record Attribute Methods
5
+ module AttributeMethods #:nodoc:
6
+ extend ActiveSupport::Concern
7
+ include ActiveModel::AttributeMethods
8
+
9
+ module ClassMethods
10
+ # Generates all the attribute related methods for columns in the database
11
+ # accessors, mutators and query methods.
12
+ def define_attribute_methods
13
+ super(columns_hash.keys)
14
+ end
15
+
16
+ # Checks whether the method is defined in the model or any of its subclasses
17
+ # that also derive from Active Record. Raises DangerousAttributeError if the
18
+ # method is defined by Active Record though.
19
+ def instance_method_already_implemented?(method_name)
20
+ method_name = method_name.to_s
21
+ @_defined_class_methods ||= ancestors.first(ancestors.index(ActiveRecord::Base)).sum([]) { |m| m.public_instance_methods(false) | m.private_instance_methods(false) | m.protected_instance_methods(false) }.map {|m| m.to_s }.to_set
22
+ @@_defined_activerecord_methods ||= defined_activerecord_methods
23
+ raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" if @@_defined_activerecord_methods.include?(method_name)
24
+ @_defined_class_methods.include?(method_name)
25
+ end
26
+
27
+ def defined_activerecord_methods
28
+ active_record = ActiveRecord::Base
29
+ super_klass = ActiveRecord::Base.superclass
30
+ methods = active_record.public_instance_methods - super_klass.public_instance_methods
31
+ methods += active_record.private_instance_methods - super_klass.private_instance_methods
32
+ methods += active_record.protected_instance_methods - super_klass.protected_instance_methods
33
+ methods.map {|m| m.to_s }.to_set
34
+ end
35
+ end
36
+
37
+ def method_missing(method_id, *args, &block)
38
+ # If we haven't generated any methods yet, generate them, then
39
+ # see if we've created the method we're looking for.
40
+ if !self.class.attribute_methods_generated?
41
+ self.class.define_attribute_methods
42
+ method_name = method_id.to_s
43
+ guard_private_attribute_method!(method_name, args)
44
+ send(method_id, *args, &block)
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ def respond_to?(*args)
51
+ self.class.define_attribute_methods unless self.class.attribute_methods_generated?
52
+ super
53
+ end
54
+
55
+ protected
56
+ def attribute_method?(attr_name)
57
+ attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name))
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveRecord
2
+ module AttributeMethods
3
+ module BeforeTypeCast
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attribute_method_suffix "_before_type_cast"
8
+ end
9
+
10
+ def read_attribute_before_type_cast(attr_name)
11
+ @attributes[attr_name]
12
+ end
13
+
14
+ # Returns a hash of attributes before typecasting and deserialization.
15
+ def attributes_before_type_cast
16
+ Hash[attribute_names.map { |name| [name, read_attribute_before_type_cast(name)] }]
17
+ end
18
+
19
+ private
20
+ # Handle *_before_type_cast for method_missing.
21
+ def attribute_before_type_cast(attribute_name)
22
+ if attribute_name == 'id'
23
+ read_attribute_before_type_cast(self.class.primary_key)
24
+ else
25
+ read_attribute_before_type_cast(attribute_name)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,95 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module Dirty
6
+ extend ActiveSupport::Concern
7
+ include ActiveModel::Dirty
8
+ include AttributeMethods::Write
9
+
10
+ included do
11
+ if self < ::ActiveRecord::Timestamp
12
+ raise "You cannot include Dirty after Timestamp"
13
+ end
14
+
15
+ superclass_delegating_accessor :partial_updates
16
+ self.partial_updates = true
17
+ end
18
+
19
+ # Attempts to +save+ the record and clears changed attributes if successful.
20
+ def save(*) #:nodoc:
21
+ if status = super
22
+ @previously_changed = changes
23
+ @changed_attributes.clear
24
+ end
25
+ status
26
+ end
27
+
28
+ # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
29
+ def save!(*) #:nodoc:
30
+ super.tap do
31
+ @previously_changed = changes
32
+ @changed_attributes.clear
33
+ end
34
+ end
35
+
36
+ # <tt>reload</tt> the record and clears changed attributes.
37
+ def reload(*) #:nodoc:
38
+ super.tap do
39
+ @previously_changed.clear
40
+ @changed_attributes.clear
41
+ end
42
+ end
43
+
44
+ private
45
+ # Wrap write_attribute to remember original attribute value.
46
+ def write_attribute(attr, value)
47
+ attr = attr.to_s
48
+
49
+ # The attribute already has an unsaved change.
50
+ if attribute_changed?(attr)
51
+ old = @changed_attributes[attr]
52
+ @changed_attributes.delete(attr) unless field_changed?(attr, old, value)
53
+ else
54
+ old = clone_attribute_value(:read_attribute, attr)
55
+ # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
56
+ old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
57
+ @changed_attributes[attr] = old if field_changed?(attr, old, value)
58
+ end
59
+
60
+ # Carry on.
61
+ super(attr, value)
62
+ end
63
+
64
+ def update(*)
65
+ if partial_updates?
66
+ # Serialized attributes should always be written in case they've been
67
+ # changed in place.
68
+ super(changed | (attributes.keys & self.class.serialized_attributes.keys))
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ def field_changed?(attr, old, value)
75
+ if column = column_for_attribute(attr)
76
+ if column.number? && column.null && (old.nil? || old == 0) && value.blank?
77
+ # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
78
+ # Hence we don't record it as a change if the value changes from nil to ''.
79
+ # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
80
+ # be typecast back to 0 (''.to_i => 0)
81
+ value = nil
82
+ else
83
+ value = column.type_cast(value)
84
+ end
85
+ end
86
+
87
+ old != value
88
+ end
89
+
90
+ def clone_with_time_zone_conversion_attribute?(attr, old)
91
+ old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,56 @@
1
+ module ActiveRecord
2
+ module AttributeMethods
3
+ module PrimaryKey
4
+ extend ActiveSupport::Concern
5
+
6
+ # Returns this record's primary key value wrapped in an Array
7
+ # or nil if the record is a new_record?
8
+ def to_key
9
+ new_record? ? nil : [ id ]
10
+ end
11
+
12
+ module ClassMethods
13
+ # Defines the primary key field -- can be overridden in subclasses. Overwriting will negate any effect of the
14
+ # primary_key_prefix_type setting, though.
15
+ def primary_key
16
+ reset_primary_key
17
+ end
18
+
19
+ # Returns a quoted version of the primary key name, used to construct SQL statements.
20
+ def quoted_primary_key
21
+ @quoted_primary_key ||= connection.quote_column_name(primary_key)
22
+ end
23
+
24
+ def reset_primary_key #:nodoc:
25
+ key = get_primary_key(base_class.name)
26
+ set_primary_key(key)
27
+ key
28
+ end
29
+
30
+ def get_primary_key(base_name) #:nodoc:
31
+ key = 'id'
32
+ case primary_key_prefix_type
33
+ when :table_name
34
+ key = base_name.to_s.foreign_key(false)
35
+ when :table_name_with_underscore
36
+ key = base_name.to_s.foreign_key
37
+ end
38
+ key
39
+ end
40
+
41
+ # Sets the name of the primary key column to use to the given value,
42
+ # or (if the value is nil or false) to the value returned by the given
43
+ # block.
44
+ #
45
+ # class Project < ActiveRecord::Base
46
+ # set_primary_key "sysid"
47
+ # end
48
+ def set_primary_key(value = nil, &block)
49
+ @quoted_primary_key = nil
50
+ define_attr_method :primary_key, value, &block
51
+ end
52
+ alias :primary_key= :set_primary_key
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module ActiveRecord
4
+ module AttributeMethods
5
+ module Query
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attribute_method_suffix "?"
10
+ end
11
+
12
+ def query_attribute(attr_name)
13
+ unless value = read_attribute(attr_name)
14
+ false
15
+ else
16
+ column = self.class.columns_hash[attr_name]
17
+ if column.nil?
18
+ if Numeric === value || value !~ /[^0-9]/
19
+ !value.to_i.zero?
20
+ else
21
+ return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
22
+ !value.blank?
23
+ end
24
+ elsif column.number?
25
+ !value.zero?
26
+ else
27
+ !value.blank?
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+ # Handle *? for method_missing.
34
+ def attribute?(attribute_name)
35
+ query_attribute(attribute_name)
36
+ end
37
+ end
38
+ end
39
+ end