activerecord 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +6023 -0
- data/README.rdoc +222 -0
- data/examples/associations.png +0 -0
- data/examples/performance.rb +162 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +124 -0
- data/lib/active_record/aggregations.rb +277 -0
- data/lib/active_record/association_preload.rb +403 -0
- data/lib/active_record/associations.rb +2254 -0
- data/lib/active_record/associations/association_collection.rb +562 -0
- data/lib/active_record/associations/association_proxy.rb +295 -0
- data/lib/active_record/associations/belongs_to_association.rb +91 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
- data/lib/active_record/associations/has_many_association.rb +128 -0
- data/lib/active_record/associations/has_many_through_association.rb +116 -0
- data/lib/active_record/associations/has_one_association.rb +143 -0
- data/lib/active_record/associations/has_one_through_association.rb +40 -0
- data/lib/active_record/associations/through_association_scope.rb +154 -0
- data/lib/active_record/attribute_methods.rb +60 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +95 -0
- data/lib/active_record/attribute_methods/primary_key.rb +50 -0
- data/lib/active_record/attribute_methods/query.rb +39 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +369 -0
- data/lib/active_record/base.rb +1867 -0
- data/lib/active_record/callbacks.rb +288 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
- data/lib/active_record/counter_cache.rb +115 -0
- data/lib/active_record/dynamic_finder_match.rb +53 -0
- data/lib/active_record/dynamic_scope_match.rb +32 -0
- data/lib/active_record/errors.rb +172 -0
- data/lib/active_record/fixtures.rb +1008 -0
- data/lib/active_record/locale/en.yml +40 -0
- data/lib/active_record/locking/optimistic.rb +172 -0
- data/lib/active_record/locking/pessimistic.rb +55 -0
- data/lib/active_record/log_subscriber.rb +48 -0
- data/lib/active_record/migration.rb +617 -0
- data/lib/active_record/named_scope.rb +138 -0
- data/lib/active_record/nested_attributes.rb +417 -0
- data/lib/active_record/observer.rb +140 -0
- data/lib/active_record/persistence.rb +291 -0
- data/lib/active_record/query_cache.rb +36 -0
- data/lib/active_record/railtie.rb +91 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +512 -0
- data/lib/active_record/reflection.rb +403 -0
- data/lib/active_record/relation.rb +393 -0
- data/lib/active_record/relation/batches.rb +89 -0
- data/lib/active_record/relation/calculations.rb +286 -0
- data/lib/active_record/relation/finder_methods.rb +355 -0
- data/lib/active_record/relation/predicate_builder.rb +41 -0
- data/lib/active_record/relation/query_methods.rb +261 -0
- data/lib/active_record/relation/spawn_methods.rb +112 -0
- data/lib/active_record/schema.rb +59 -0
- data/lib/active_record/schema_dumper.rb +195 -0
- data/lib/active_record/serialization.rb +60 -0
- data/lib/active_record/serializers/xml_serializer.rb +244 -0
- data/lib/active_record/session_store.rb +340 -0
- data/lib/active_record/test_case.rb +67 -0
- data/lib/active_record/timestamp.rb +88 -0
- data/lib/active_record/transactions.rb +356 -0
- data/lib/active_record/validations.rb +84 -0
- data/lib/active_record/validations/associated.rb +48 -0
- data/lib/active_record/validations/uniqueness.rb +185 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/rails/generators/active_record.rb +27 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
- data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
- data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
- metadata +224 -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_sql(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_sql(@reflection.through_reflection.klass.send(:sanitize_sql, conditions)).gsub(
|
140
|
+
@reflection.quoted_table_name,
|
141
|
+
@reflection.through_reflection.quoted_table_name)
|
142
|
+
elsif conditions
|
143
|
+
interpolate_sql(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
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def attribute_method?(attr_name)
|
57
|
+
attr_name == 'id' || attributes.include?(attr_name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,33 @@
|
|
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
|
+
self.attribute_names.inject({}) do |attrs, name|
|
17
|
+
attrs[name] = read_attribute_before_type_cast(name)
|
18
|
+
attrs
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
# Handle *_before_type_cast for method_missing.
|
24
|
+
def attribute_before_type_cast(attribute_name)
|
25
|
+
if attribute_name == 'id'
|
26
|
+
read_attribute_before_type_cast(self.class.primary_key)
|
27
|
+
else
|
28
|
+
read_attribute_before_type_cast(attribute_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
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,50 @@
|
|
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
|
+
def reset_primary_key #:nodoc:
|
20
|
+
key = get_primary_key(base_class.name)
|
21
|
+
set_primary_key(key)
|
22
|
+
key
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_primary_key(base_name) #:nodoc:
|
26
|
+
key = 'id'
|
27
|
+
case primary_key_prefix_type
|
28
|
+
when :table_name
|
29
|
+
key = base_name.to_s.foreign_key(false)
|
30
|
+
when :table_name_with_underscore
|
31
|
+
key = base_name.to_s.foreign_key
|
32
|
+
end
|
33
|
+
key
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sets the name of the primary key column to use to the given value,
|
37
|
+
# or (if the value is nil or false) to the value returned by the given
|
38
|
+
# block.
|
39
|
+
#
|
40
|
+
# class Project < ActiveRecord::Base
|
41
|
+
# set_primary_key "sysid"
|
42
|
+
# end
|
43
|
+
def set_primary_key(value = nil, &block)
|
44
|
+
define_attr_method :primary_key, value, &block
|
45
|
+
end
|
46
|
+
alias :primary_key= :set_primary_key
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
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
|