composite_primary_keys 7.0.13 → 7.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.rdoc +615 -608
- data/lib/composite_primary_keys.rb +110 -110
- data/lib/composite_primary_keys/associations/association.rb +23 -23
- data/lib/composite_primary_keys/associations/association_scope.rb +77 -77
- data/lib/composite_primary_keys/associations/has_and_belongs_to_many_association.rb +59 -59
- data/lib/composite_primary_keys/associations/has_many_association.rb +56 -56
- data/lib/composite_primary_keys/associations/join_dependency.rb +89 -89
- data/lib/composite_primary_keys/associations/join_dependency/join_part.rb +38 -38
- data/lib/composite_primary_keys/associations/preloader/association.rb +78 -78
- data/lib/composite_primary_keys/associations/preloader/has_and_belongs_to_many.rb +46 -46
- data/lib/composite_primary_keys/attribute_methods/dirty.rb +26 -26
- data/lib/composite_primary_keys/attribute_methods/read.rb +34 -34
- data/lib/composite_primary_keys/attribute_methods/write.rb +36 -36
- data/lib/composite_primary_keys/base.rb +0 -6
- data/lib/composite_primary_keys/composite_arrays.rb +30 -30
- data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb +4 -2
- data/lib/composite_primary_keys/connection_adapters/sqlserver_adapter.rb +17 -0
- data/lib/composite_primary_keys/core.rb +47 -47
- data/lib/composite_primary_keys/persistence.rb +60 -60
- data/lib/composite_primary_keys/relation.rb +56 -56
- data/lib/composite_primary_keys/relation/calculations.rb +75 -65
- data/lib/composite_primary_keys/relation/finder_methods.rb +196 -196
- data/lib/composite_primary_keys/sanitization.rb +52 -52
- data/lib/composite_primary_keys/validations/uniqueness.rb +37 -39
- data/lib/composite_primary_keys/version.rb +8 -8
- data/tasks/databases/sqlserver.rake +40 -27
- data/test/connections/databases.example.yml +18 -18
- data/test/connections/native_sqlserver/connection.rb +14 -11
- data/test/fixtures/db_definitions/mysql.sql +208 -208
- data/test/fixtures/db_definitions/postgresql.sql +210 -210
- data/test/fixtures/db_definitions/sqlite.sql +197 -197
- data/test/fixtures/db_definitions/sqlserver.drop.sql +94 -91
- data/test/fixtures/db_definitions/sqlserver.sql +232 -226
- data/test/fixtures/employee.rb +5 -5
- data/test/test_associations.rb +275 -275
- data/test/test_attributes.rb +60 -60
- data/test/test_create.rb +112 -112
- data/test/test_delete.rb +152 -148
- data/test/test_delete_all.rb +21 -21
- data/test/test_enum.rb +20 -20
- data/test/test_equal.rb +1 -1
- data/test/test_tutorial_example.rb +21 -21
- metadata +3 -2
@@ -1,46 +1,46 @@
|
|
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 = build_scope.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
|
+
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 = build_scope.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,26 +1,26 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
module Dirty
|
4
|
-
def write_attribute(attr, value)
|
5
|
-
# CPK
|
6
|
-
if attr.kind_of?(Array)
|
7
|
-
# A *composite* attribute can't be marked as changed! So do nothing now.
|
8
|
-
# We will come back in here with an *individual* attribute when Write#write_attribute looks through the individual attributes comprising this composite key:
|
9
|
-
# [attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
|
10
|
-
else
|
11
|
-
attr = attr.to_s
|
12
|
-
|
13
|
-
save_changed_attribute(attr, value)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Carry on.
|
17
|
-
super(attr, value)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
ActiveRecord::Base.class_eval do
|
24
|
-
alias :[]= :write_attribute
|
25
|
-
public :[]=
|
26
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Dirty
|
4
|
+
def write_attribute(attr, value)
|
5
|
+
# CPK
|
6
|
+
if attr.kind_of?(Array)
|
7
|
+
# A *composite* attribute can't be marked as changed! So do nothing now.
|
8
|
+
# We will come back in here with an *individual* attribute when Write#write_attribute looks through the individual attributes comprising this composite key:
|
9
|
+
# [attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
|
10
|
+
else
|
11
|
+
attr = attr.to_s
|
12
|
+
|
13
|
+
save_changed_attribute(attr, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Carry on.
|
17
|
+
super(attr, value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ActiveRecord::Base.class_eval do
|
24
|
+
alias :[]= :write_attribute
|
25
|
+
public :[]=
|
26
|
+
end
|
@@ -1,35 +1,35 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
module Read
|
4
|
-
def read_attribute(attr_name)
|
5
|
-
if attr_name.kind_of?(Array)
|
6
|
-
attr_name.map {|name| read_attribute(name)}.to_composite_keys
|
7
|
-
else
|
8
|
-
# If it's cached, just return it
|
9
|
-
# We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
|
10
|
-
name = attr_name.to_s
|
11
|
-
@attributes_cache[name] || @attributes_cache.fetch(name) {
|
12
|
-
column = @column_types_override[name] if @column_types_override
|
13
|
-
column ||= @column_types[name]
|
14
|
-
|
15
|
-
return @attributes.fetch(name) {
|
16
|
-
if name == 'id' && self.class.primary_key != name
|
17
|
-
read_attribute(self.class.primary_key)
|
18
|
-
end
|
19
|
-
} unless column
|
20
|
-
|
21
|
-
value = @attributes.fetch(name) {
|
22
|
-
return block_given? ? yield(name) : nil
|
23
|
-
}
|
24
|
-
|
25
|
-
if self.class.cache_attribute?(name)
|
26
|
-
@attributes_cache[name] = column.type_cast(value)
|
27
|
-
else
|
28
|
-
column.type_cast value
|
29
|
-
end
|
30
|
-
}
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Read
|
4
|
+
def read_attribute(attr_name)
|
5
|
+
if attr_name.kind_of?(Array)
|
6
|
+
attr_name.map {|name| read_attribute(name)}.to_composite_keys
|
7
|
+
else
|
8
|
+
# If it's cached, just return it
|
9
|
+
# We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
|
10
|
+
name = attr_name.to_s
|
11
|
+
@attributes_cache[name] || @attributes_cache.fetch(name) {
|
12
|
+
column = @column_types_override[name] if @column_types_override
|
13
|
+
column ||= @column_types[name]
|
14
|
+
|
15
|
+
return @attributes.fetch(name) {
|
16
|
+
if name == 'id' && self.class.primary_key != name
|
17
|
+
read_attribute(self.class.primary_key)
|
18
|
+
end
|
19
|
+
} unless column
|
20
|
+
|
21
|
+
value = @attributes.fetch(name) {
|
22
|
+
return block_given? ? yield(name) : nil
|
23
|
+
}
|
24
|
+
|
25
|
+
if self.class.cache_attribute?(name)
|
26
|
+
@attributes_cache[name] = column.type_cast(value)
|
27
|
+
else
|
28
|
+
column.type_cast value
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
35
|
end
|
@@ -1,36 +1,36 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module AttributeMethods
|
3
|
-
module Write
|
4
|
-
def write_attribute_with_type_cast(attr_name, value, type_cast_method)
|
5
|
-
# CPK
|
6
|
-
if attr_name.kind_of?(Array)
|
7
|
-
value = [nil]*attr_name.length if value.nil?
|
8
|
-
unless value.length == attr_name.length
|
9
|
-
raise "Number of attr_names #{attr_name.inspect} and values #{value.inspect} do not match"
|
10
|
-
end
|
11
|
-
[attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
|
12
|
-
value
|
13
|
-
else
|
14
|
-
attr_name = attr_name.to_s
|
15
|
-
# CPK
|
16
|
-
# attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
|
17
|
-
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key && !self.composite?
|
18
|
-
@attributes_cache.delete(attr_name)
|
19
|
-
column = column_for_attribute(attr_name)
|
20
|
-
|
21
|
-
# If we're dealing with a binary column, write the data to the cache
|
22
|
-
# so we don't attempt to typecast multiple times.
|
23
|
-
if column && column.binary?
|
24
|
-
@attributes_cache[attr_name] = value
|
25
|
-
end
|
26
|
-
|
27
|
-
if column || @attributes.has_key?(attr_name)
|
28
|
-
@attributes[attr_name] = send(type_cast_method, column, value)
|
29
|
-
else
|
30
|
-
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeMethods
|
3
|
+
module Write
|
4
|
+
def write_attribute_with_type_cast(attr_name, value, type_cast_method)
|
5
|
+
# CPK
|
6
|
+
if attr_name.kind_of?(Array)
|
7
|
+
value = [nil]*attr_name.length if value.nil?
|
8
|
+
unless value.length == attr_name.length
|
9
|
+
raise "Number of attr_names #{attr_name.inspect} and values #{value.inspect} do not match"
|
10
|
+
end
|
11
|
+
[attr_name, value].transpose.map {|name,val| write_attribute(name, val)}
|
12
|
+
value
|
13
|
+
else
|
14
|
+
attr_name = attr_name.to_s
|
15
|
+
# CPK
|
16
|
+
# attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
|
17
|
+
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key && !self.composite?
|
18
|
+
@attributes_cache.delete(attr_name)
|
19
|
+
column = column_for_attribute(attr_name)
|
20
|
+
|
21
|
+
# If we're dealing with a binary column, write the data to the cache
|
22
|
+
# so we don't attempt to typecast multiple times.
|
23
|
+
if column && column.binary?
|
24
|
+
@attributes_cache[attr_name] = value
|
25
|
+
end
|
26
|
+
|
27
|
+
if column || @attributes.has_key?(attr_name)
|
28
|
+
@attributes[attr_name] = send(type_cast_method, column, value)
|
29
|
+
else
|
30
|
+
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -111,12 +111,6 @@ module ActiveRecord
|
|
111
111
|
id
|
112
112
|
end
|
113
113
|
|
114
|
-
def ==(comparison_object)
|
115
|
-
return false if !persisted? && comparison_object.object_id != object_id
|
116
|
-
return true if equal? comparison_object
|
117
|
-
ids.is_a?(Array) ? super(comparison_object) && ids.all? {|id| !id.nil?} : super(comparison_object)
|
118
|
-
end
|
119
|
-
|
120
114
|
def can_change_primary_key_values?
|
121
115
|
false
|
122
116
|
end
|
@@ -1,30 +1,30 @@
|
|
1
|
-
module CompositePrimaryKeys
|
2
|
-
ID_SEP = ','
|
3
|
-
ID_SET_SEP = ';'
|
4
|
-
|
5
|
-
module ArrayExtension
|
6
|
-
def to_composite_keys
|
7
|
-
CompositeKeys.new(self)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class CompositeKeys < Array
|
12
|
-
def self.parse(value)
|
13
|
-
case value
|
14
|
-
when Array
|
15
|
-
value.to_composite_keys
|
16
|
-
when String
|
17
|
-
self.new(value.split(ID_SEP))
|
18
|
-
else
|
19
|
-
raise(ArgumentError, "Unsupported type: #{value}")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_s
|
24
|
-
# Doing this makes it easier to parse Base#[](attr_name)
|
25
|
-
join(ID_SEP)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
Array.send(:include, CompositePrimaryKeys::ArrayExtension)
|
1
|
+
module CompositePrimaryKeys
|
2
|
+
ID_SEP = ','
|
3
|
+
ID_SET_SEP = ';'
|
4
|
+
|
5
|
+
module ArrayExtension
|
6
|
+
def to_composite_keys
|
7
|
+
CompositeKeys.new(self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class CompositeKeys < Array
|
12
|
+
def self.parse(value)
|
13
|
+
case value
|
14
|
+
when Array
|
15
|
+
value.to_composite_keys
|
16
|
+
when String
|
17
|
+
self.new(value.split(ID_SEP))
|
18
|
+
else
|
19
|
+
raise(ArgumentError, "Unsupported type: #{value}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
# Doing this makes it easier to parse Base#[](attr_name)
|
25
|
+
join(ID_SEP)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Array.send(:include, CompositePrimaryKeys::ArrayExtension)
|
data/lib/composite_primary_keys/connection_adapters/abstract/connection_specification_changes.rb
CHANGED
@@ -4,6 +4,9 @@ module ActiveRecord
|
|
4
4
|
if (adapter.to_s =~ /postgresql/) or (adapter.to_s =~ /postgis/)
|
5
5
|
require "composite_primary_keys/connection_adapters/postgresql_adapter.rb"
|
6
6
|
end
|
7
|
+
if (adapter.to_s =~ /sqlserver/)
|
8
|
+
require "composite_primary_keys/connection_adapters/sqlserver_adapter.rb"
|
9
|
+
end
|
7
10
|
end
|
8
11
|
|
9
12
|
def self.establish_connection(spec = ENV["DATABASE_URL"])
|
@@ -61,8 +64,7 @@ module ActiveRecord
|
|
61
64
|
connection_handler.clear_active_connections!
|
62
65
|
end
|
63
66
|
|
64
|
-
delegate :clear_reloadable_connections!,
|
65
|
-
:clear_all_connections!,:verify_active_connections!, :to => :connection_handler
|
67
|
+
delegate :clear_reloadable_connections!, :clear_all_connections!, :to => :connection_handler
|
66
68
|
end
|
67
69
|
end
|
68
70
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class SQLServerAdapter
|
4
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
5
|
+
sql =
|
6
|
+
if pk
|
7
|
+
# support composite primary keys consisting of more than one column name
|
8
|
+
inserted_pks = [pk].flatten.map {|pk| "inserted.#{pk}"}
|
9
|
+
sql.insert(sql.index(/ (DEFAULT )?VALUES/), " OUTPUT #{inserted_pks.join(", ")}")
|
10
|
+
else
|
11
|
+
"#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
12
|
+
end
|
13
|
+
[sql, binds]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,47 +1,47 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Core
|
3
|
-
def init_internals
|
4
|
-
pk = self.class.primary_key
|
5
|
-
|
6
|
-
# CPK
|
7
|
-
#@attributes[pk] = nil unless @attributes.key?(pk)
|
8
|
-
unless self.composite?
|
9
|
-
@attributes[pk] = nil unless @attributes.key?(pk)
|
10
|
-
end
|
11
|
-
|
12
|
-
@aggregation_cache = {}
|
13
|
-
@association_cache = {}
|
14
|
-
@attributes_cache = {}
|
15
|
-
@readonly = false
|
16
|
-
@destroyed = false
|
17
|
-
@marked_for_destruction = false
|
18
|
-
@destroyed_by_association = nil
|
19
|
-
@new_record = true
|
20
|
-
@txn = nil
|
21
|
-
@_start_transaction_state = {}
|
22
|
-
@transaction_state = nil
|
23
|
-
@reflects_state = [false]
|
24
|
-
end
|
25
|
-
|
26
|
-
def initialize_dup(other) # :nodoc:
|
27
|
-
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
|
28
|
-
self.class.initialize_attributes(cloned_attributes, :serialized => false)
|
29
|
-
|
30
|
-
@attributes = cloned_attributes
|
31
|
-
|
32
|
-
# CPK
|
33
|
-
#@attributes[self.class.primary_key] = nil
|
34
|
-
Array(self.class.primary_key).each {|key| @attributes[key] = nil}
|
35
|
-
|
36
|
-
run_callbacks(:initialize) unless _initialize_callbacks.empty?
|
37
|
-
|
38
|
-
@aggregation_cache = {}
|
39
|
-
@association_cache = {}
|
40
|
-
@attributes_cache = {}
|
41
|
-
|
42
|
-
@new_record = true
|
43
|
-
|
44
|
-
super
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
1
|
+
module ActiveRecord
|
2
|
+
module Core
|
3
|
+
def init_internals
|
4
|
+
pk = self.class.primary_key
|
5
|
+
|
6
|
+
# CPK
|
7
|
+
#@attributes[pk] = nil unless @attributes.key?(pk)
|
8
|
+
unless self.composite?
|
9
|
+
@attributes[pk] = nil unless @attributes.key?(pk)
|
10
|
+
end
|
11
|
+
|
12
|
+
@aggregation_cache = {}
|
13
|
+
@association_cache = {}
|
14
|
+
@attributes_cache = {}
|
15
|
+
@readonly = false
|
16
|
+
@destroyed = false
|
17
|
+
@marked_for_destruction = false
|
18
|
+
@destroyed_by_association = nil
|
19
|
+
@new_record = true
|
20
|
+
@txn = nil
|
21
|
+
@_start_transaction_state = {}
|
22
|
+
@transaction_state = nil
|
23
|
+
@reflects_state = [false]
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize_dup(other) # :nodoc:
|
27
|
+
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
|
28
|
+
self.class.initialize_attributes(cloned_attributes, :serialized => false)
|
29
|
+
|
30
|
+
@attributes = cloned_attributes
|
31
|
+
|
32
|
+
# CPK
|
33
|
+
#@attributes[self.class.primary_key] = nil
|
34
|
+
Array(self.class.primary_key).each {|key| @attributes[key] = nil}
|
35
|
+
|
36
|
+
run_callbacks(:initialize) unless _initialize_callbacks.empty?
|
37
|
+
|
38
|
+
@aggregation_cache = {}
|
39
|
+
@association_cache = {}
|
40
|
+
@attributes_cache = {}
|
41
|
+
|
42
|
+
@new_record = true
|
43
|
+
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|