bigrecord 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +44 -0
- data/Rakefile +17 -0
- data/VERSION +1 -0
- data/doc/bigrecord_specs.rdoc +36 -0
- data/doc/getting_started.rdoc +157 -0
- data/examples/bigrecord.yml +25 -0
- data/generators/bigrecord/bigrecord_generator.rb +17 -0
- data/generators/bigrecord/templates/bigrecord.rake +47 -0
- data/generators/bigrecord_migration/bigrecord_migration_generator.rb +13 -0
- data/generators/bigrecord_migration/templates/migration.rb +9 -0
- data/generators/bigrecord_model/bigrecord_model_generator.rb +28 -0
- data/generators/bigrecord_model/templates/migration.rb +13 -0
- data/generators/bigrecord_model/templates/model.rb +7 -0
- data/generators/bigrecord_model/templates/model_spec.rb +12 -0
- data/init.rb +9 -0
- data/install.rb +22 -0
- data/lib/big_record/abstract_base.rb +1088 -0
- data/lib/big_record/action_view_extensions.rb +266 -0
- data/lib/big_record/ar_associations/association_collection.rb +194 -0
- data/lib/big_record/ar_associations/association_proxy.rb +158 -0
- data/lib/big_record/ar_associations/belongs_to_association.rb +57 -0
- data/lib/big_record/ar_associations/belongs_to_many_association.rb +57 -0
- data/lib/big_record/ar_associations/has_and_belongs_to_many_association.rb +164 -0
- data/lib/big_record/ar_associations/has_many_association.rb +191 -0
- data/lib/big_record/ar_associations/has_one_association.rb +80 -0
- data/lib/big_record/ar_associations.rb +1608 -0
- data/lib/big_record/ar_reflection.rb +223 -0
- data/lib/big_record/attribute_methods.rb +75 -0
- data/lib/big_record/base.rb +618 -0
- data/lib/big_record/br_associations/association_collection.rb +194 -0
- data/lib/big_record/br_associations/association_proxy.rb +153 -0
- data/lib/big_record/br_associations/belongs_to_association.rb +52 -0
- data/lib/big_record/br_associations/belongs_to_many_association.rb +293 -0
- data/lib/big_record/br_associations/cached_item_proxy.rb +194 -0
- data/lib/big_record/br_associations/cached_item_proxy_factory.rb +62 -0
- data/lib/big_record/br_associations/has_and_belongs_to_many_association.rb +168 -0
- data/lib/big_record/br_associations/has_one_association.rb +80 -0
- data/lib/big_record/br_associations.rb +978 -0
- data/lib/big_record/br_reflection.rb +151 -0
- data/lib/big_record/callbacks.rb +367 -0
- data/lib/big_record/connection_adapters/abstract/connection_specification.rb +279 -0
- data/lib/big_record/connection_adapters/abstract/database_statements.rb +175 -0
- data/lib/big_record/connection_adapters/abstract/quoting.rb +58 -0
- data/lib/big_record/connection_adapters/abstract_adapter.rb +190 -0
- data/lib/big_record/connection_adapters/column.rb +491 -0
- data/lib/big_record/connection_adapters/hbase_adapter.rb +432 -0
- data/lib/big_record/connection_adapters/view.rb +27 -0
- data/lib/big_record/connection_adapters.rb +10 -0
- data/lib/big_record/deletion.rb +73 -0
- data/lib/big_record/dynamic_schema.rb +92 -0
- data/lib/big_record/embedded.rb +71 -0
- data/lib/big_record/embedded_associations/association_proxy.rb +148 -0
- data/lib/big_record/family_span_columns.rb +89 -0
- data/lib/big_record/fixtures.rb +1025 -0
- data/lib/big_record/migration.rb +380 -0
- data/lib/big_record/routing_ext.rb +65 -0
- data/lib/big_record/timestamp.rb +51 -0
- data/lib/big_record/validations.rb +830 -0
- data/lib/big_record.rb +125 -0
- data/lib/bigrecord.rb +1 -0
- data/rails/init.rb +9 -0
- data/spec/connections/bigrecord.yml +13 -0
- data/spec/connections/cassandra/connection.rb +2 -0
- data/spec/connections/hbase/connection.rb +2 -0
- data/spec/debug.log +281 -0
- data/spec/integration/br_associations_spec.rb +80 -0
- data/spec/lib/animal.rb +12 -0
- data/spec/lib/book.rb +10 -0
- data/spec/lib/broken_migrations/duplicate_name/20090706182535_add_animals_table.rb +14 -0
- data/spec/lib/broken_migrations/duplicate_name/20090706193019_add_animals_table.rb +9 -0
- data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_books_table.rb +9 -0
- data/spec/lib/broken_migrations/duplicate_version/20090706190623_add_companies_table.rb +9 -0
- data/spec/lib/company.rb +14 -0
- data/spec/lib/embedded/web_link.rb +12 -0
- data/spec/lib/employee.rb +33 -0
- data/spec/lib/migrations/20090706182535_add_animals_table.rb +13 -0
- data/spec/lib/migrations/20090706190623_add_books_table.rb +15 -0
- data/spec/lib/migrations/20090706193019_add_companies_table.rb +14 -0
- data/spec/lib/migrations/20090706194512_add_employees_table.rb +13 -0
- data/spec/lib/migrations/20090706195741_add_zoos_table.rb +13 -0
- data/spec/lib/novel.rb +5 -0
- data/spec/lib/zoo.rb +17 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +55 -0
- data/spec/unit/abstract_base_spec.rb +287 -0
- data/spec/unit/adapters/abstract_adapter_spec.rb +56 -0
- data/spec/unit/adapters/adapter_shared_spec.rb +51 -0
- data/spec/unit/adapters/hbase_adapter_spec.rb +15 -0
- data/spec/unit/ar_associations_spec.rb +8 -0
- data/spec/unit/base_spec.rb +6 -0
- data/spec/unit/br_associations_spec.rb +58 -0
- data/spec/unit/embedded_spec.rb +43 -0
- data/spec/unit/find_spec.rb +34 -0
- data/spec/unit/hash_helper_spec.rb +44 -0
- data/spec/unit/migration_spec.rb +144 -0
- data/spec/unit/model_spec.rb +315 -0
- data/spec/unit/validations_spec.rb +182 -0
- data/tasks/bigrecord_tasks.rake +47 -0
- data/tasks/data_store.rb +46 -0
- data/tasks/gem.rb +22 -0
- data/tasks/rdoc.rb +8 -0
- data/tasks/spec.rb +34 -0
- metadata +189 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module ArAssociations
|
3
|
+
class BelongsToManyAssociation < AssociationProxy #:nodoc:
|
4
|
+
|
5
|
+
def create(attributes = {})
|
6
|
+
replace(@reflection.klass.create(attributes))
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(attributes = {})
|
10
|
+
replace(@reflection.klass.new(attributes))
|
11
|
+
end
|
12
|
+
|
13
|
+
def replace(record)
|
14
|
+
counter_cache_name = @reflection.counter_cache_column
|
15
|
+
|
16
|
+
if record.nil?
|
17
|
+
if counter_cache_name && @owner[counter_cache_name] && !@owner.new_record?
|
18
|
+
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
|
19
|
+
end
|
20
|
+
|
21
|
+
@target = @owner[@reflection.primary_key_name] = nil
|
22
|
+
else
|
23
|
+
raise_on_type_mismatch(record)
|
24
|
+
|
25
|
+
if counter_cache_name && !@owner.new_record?
|
26
|
+
@reflection.klass.increment_counter(counter_cache_name, record.id)
|
27
|
+
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
|
28
|
+
end
|
29
|
+
|
30
|
+
@target = (AssociationProxy === record ? record.target : record)
|
31
|
+
@owner[@reflection.primary_key_name] = record.id unless record.new_record?
|
32
|
+
@updated = true
|
33
|
+
end
|
34
|
+
|
35
|
+
loaded
|
36
|
+
record
|
37
|
+
end
|
38
|
+
|
39
|
+
def updated?
|
40
|
+
@updated
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def find_target
|
45
|
+
@reflection.klass.find(
|
46
|
+
@owner[@reflection.primary_key_name],
|
47
|
+
:conditions => conditions,
|
48
|
+
:include => @reflection.options[:include]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def foreign_key_present
|
53
|
+
!@owner[@reflection.primary_key_name].nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module ArAssociations
|
3
|
+
class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
|
4
|
+
def initialize(owner, reflection)
|
5
|
+
super
|
6
|
+
construct_sql
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(attributes = {})
|
10
|
+
load_target
|
11
|
+
build_record(attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create(attributes = {})
|
15
|
+
create_record(attributes) { |record| insert_record(record) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def create!(attributes = {})
|
19
|
+
create_record(attributes) { |record| insert_record(record, true) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_first
|
23
|
+
load_target.first
|
24
|
+
end
|
25
|
+
|
26
|
+
def find(*args)
|
27
|
+
options = args.extract_options!
|
28
|
+
|
29
|
+
# If using a custom finder_sql, scan the entire collection.
|
30
|
+
if @reflection.options[:finder_sql]
|
31
|
+
expects_array = args.first.kind_of?(Array)
|
32
|
+
ids = args.flatten.compact.uniq
|
33
|
+
|
34
|
+
if ids.size == 1
|
35
|
+
id = ids.first.to_i
|
36
|
+
record = load_target.detect { |record| id == record.id }
|
37
|
+
expects_array ? [record] : record
|
38
|
+
else
|
39
|
+
load_target.select { |record| ids.include?(record.id) }
|
40
|
+
end
|
41
|
+
else
|
42
|
+
conditions = "#{@finder_sql}"
|
43
|
+
|
44
|
+
if sanitized_conditions = sanitize_sql(options[:conditions])
|
45
|
+
conditions << " AND (#{sanitized_conditions})"
|
46
|
+
end
|
47
|
+
|
48
|
+
options[:conditions] = conditions
|
49
|
+
options[:joins] = @join_sql
|
50
|
+
options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
|
51
|
+
|
52
|
+
if options[:order] && @reflection.options[:order]
|
53
|
+
options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
|
54
|
+
elsif @reflection.options[:order]
|
55
|
+
options[:order] = @reflection.options[:order]
|
56
|
+
end
|
57
|
+
|
58
|
+
merge_options_from_reflection!(options)
|
59
|
+
|
60
|
+
options[:select] ||= (@reflection.options[:select] || '*')
|
61
|
+
|
62
|
+
# Pass through args exactly as we received them.
|
63
|
+
args << options
|
64
|
+
@reflection.klass.find(*args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
def count_records
|
70
|
+
load_target.size rescue 0
|
71
|
+
end
|
72
|
+
|
73
|
+
def insert_record(record, force=true)
|
74
|
+
if record.new_record?
|
75
|
+
if force
|
76
|
+
record.save!
|
77
|
+
else
|
78
|
+
return false unless record.save
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if @reflection.options[:insert_sql]
|
83
|
+
@reflection.klass.connection.execute(interpolate_sql(@reflection.options[:insert_sql], record))
|
84
|
+
else
|
85
|
+
columns = @reflection.klass.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
|
86
|
+
|
87
|
+
attributes = columns.inject({}) do |attributes, column|
|
88
|
+
case column.name
|
89
|
+
when @reflection.primary_key_name
|
90
|
+
attributes[column.name] = @owner.quoted_id
|
91
|
+
when @reflection.association_foreign_key
|
92
|
+
attributes[column.name] = record.quoted_id
|
93
|
+
else
|
94
|
+
if record.attributes.has_key?(column.name)
|
95
|
+
value = @owner.send(:quote_value, record[column.name], column)
|
96
|
+
attributes[column.name] = value unless value.nil?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
attributes
|
100
|
+
end
|
101
|
+
|
102
|
+
sql =
|
103
|
+
"INSERT INTO #{@reflection.options[:join_table]} (#{record.send(:quoted_column_names, attributes).join(', ')}) " +
|
104
|
+
"VALUES (#{attributes.values.join(', ')})"
|
105
|
+
|
106
|
+
@reflection.klass.connection.execute(sql)
|
107
|
+
end
|
108
|
+
|
109
|
+
return true
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete_records(records)
|
113
|
+
if sql = @reflection.options[:delete_sql]
|
114
|
+
records.each { |record| @owner.connection.execute(interpolate_sql(sql, record)) }
|
115
|
+
else
|
116
|
+
ids = quoted_record_ids(records)
|
117
|
+
sql = "DELETE FROM #{@reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
|
118
|
+
@reflection.klass.connection.execute(sql)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def construct_sql
|
123
|
+
interpolate_sql_options!(@reflection.options, :finder_sql)
|
124
|
+
|
125
|
+
if @reflection.options[:finder_sql]
|
126
|
+
@finder_sql = @reflection.options[:finder_sql]
|
127
|
+
else
|
128
|
+
@finder_sql = "#{@reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
|
129
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
130
|
+
end
|
131
|
+
|
132
|
+
@join_sql = "INNER JOIN #{@reflection.options[:join_table]} ON #{@reflection.klass.table_name}.#{@reflection.klass.primary_key} = #{@reflection.options[:join_table]}.#{@reflection.association_foreign_key}"
|
133
|
+
end
|
134
|
+
|
135
|
+
def construct_scope
|
136
|
+
{ :find => { :conditions => @finder_sql,
|
137
|
+
:joins => @join_sql,
|
138
|
+
:readonly => false,
|
139
|
+
:order => @reflection.options[:order],
|
140
|
+
:limit => @reflection.options[:limit] } }
|
141
|
+
end
|
142
|
+
|
143
|
+
# Join tables with additional columns on top of the two foreign keys must be considered ambiguous unless a select
|
144
|
+
# clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has
|
145
|
+
# an id column. This will then overwrite the id column of the records coming back.
|
146
|
+
def finding_with_ambiguous_select?(select_clause)
|
147
|
+
!select_clause && @reflection.klass.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
def create_record(attributes)
|
152
|
+
# Can't use Base.create because the foreign key may be a protected attribute.
|
153
|
+
ensure_owner_is_not_new
|
154
|
+
if attributes.is_a?(Array)
|
155
|
+
attributes.collect { |attr| create(attr) }
|
156
|
+
else
|
157
|
+
record = build(attributes)
|
158
|
+
yield(record)
|
159
|
+
record
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module ArAssociations
|
3
|
+
class HasManyAssociation < AssociationCollection #:nodoc:
|
4
|
+
def initialize(owner, reflection)
|
5
|
+
super
|
6
|
+
construct_sql
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(attributes = {})
|
10
|
+
if attributes.is_a?(Array)
|
11
|
+
attributes.collect { |attr| build(attr) }
|
12
|
+
else
|
13
|
+
record = @reflection.klass.new(attributes)
|
14
|
+
set_belongs_to_association_for(record)
|
15
|
+
|
16
|
+
@target ||= [] unless loaded?
|
17
|
+
@target << record
|
18
|
+
|
19
|
+
record
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Count the number of associated records. All arguments are optional.
|
24
|
+
def count(*args)
|
25
|
+
if @reflection.options[:counter_sql]
|
26
|
+
@reflection.klass.count_by_sql(@counter_sql)
|
27
|
+
elsif @reflection.options[:finder_sql]
|
28
|
+
@reflection.klass.count_by_sql(@finder_sql)
|
29
|
+
else
|
30
|
+
column_name, options = @reflection.klass.send(:construct_count_options_from_legacy_args, *args)
|
31
|
+
options[:conditions] = options[:conditions].nil? ?
|
32
|
+
@finder_sql :
|
33
|
+
@finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
|
34
|
+
options[:include] = @reflection.options[:include]
|
35
|
+
|
36
|
+
@reflection.klass.count(column_name, options)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def find(*args)
|
41
|
+
options = Base.send(:extract_options_from_args!, args)
|
42
|
+
|
43
|
+
# If using a custom finder_sql, scan the entire collection.
|
44
|
+
if @reflection.options[:finder_sql]
|
45
|
+
expects_array = args.first.kind_of?(Array)
|
46
|
+
ids = args.flatten.compact.uniq
|
47
|
+
|
48
|
+
if ids.size == 1
|
49
|
+
id = ids.first
|
50
|
+
record = load_target.detect { |record| id == record.id }
|
51
|
+
expects_array ? [ record ] : record
|
52
|
+
else
|
53
|
+
load_target.select { |record| ids.include?(record.id) }
|
54
|
+
end
|
55
|
+
else
|
56
|
+
conditions = "#{@finder_sql}"
|
57
|
+
if sanitized_conditions = sanitize_sql(options[:conditions])
|
58
|
+
conditions << " AND (#{sanitized_conditions})"
|
59
|
+
end
|
60
|
+
options[:conditions] = conditions
|
61
|
+
|
62
|
+
if options[:order] && @reflection.options[:order]
|
63
|
+
options[:order] = "#{options[:order]}, #{@reflection.options[:order]}"
|
64
|
+
elsif @reflection.options[:order]
|
65
|
+
options[:order] = @reflection.options[:order]
|
66
|
+
end
|
67
|
+
|
68
|
+
merge_options_from_reflection!(options)
|
69
|
+
|
70
|
+
# Pass through args exactly as we received them.
|
71
|
+
args << options
|
72
|
+
@reflection.klass.find(*args)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
def method_missing(method, *args, &block)
|
78
|
+
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
79
|
+
super
|
80
|
+
else
|
81
|
+
create_scoping = {}
|
82
|
+
set_belongs_to_association_for(create_scoping)
|
83
|
+
|
84
|
+
@reflection.klass.with_scope(
|
85
|
+
:create => create_scoping,
|
86
|
+
:find => {
|
87
|
+
:conditions => @finder_sql,
|
88
|
+
:joins => @join_sql,
|
89
|
+
:readonly => false
|
90
|
+
}
|
91
|
+
) do
|
92
|
+
@reflection.klass.send(method, *args, &block)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def load_target
|
98
|
+
if !@owner.new_record? || foreign_key_present
|
99
|
+
begin
|
100
|
+
if !loaded?
|
101
|
+
if @target.is_a?(Array) && @target.any?
|
102
|
+
@target = (find_target + @target).uniq
|
103
|
+
else
|
104
|
+
@target = find_target
|
105
|
+
end
|
106
|
+
end
|
107
|
+
rescue ActiveRecord::RecordNotFound
|
108
|
+
reset
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
loaded if target
|
113
|
+
target
|
114
|
+
end
|
115
|
+
|
116
|
+
def count_records
|
117
|
+
count = if has_cached_counter?
|
118
|
+
@owner.send(:read_attribute, cached_counter_attribute_name)
|
119
|
+
elsif @reflection.options[:counter_sql]
|
120
|
+
@reflection.klass.count_by_sql(@counter_sql)
|
121
|
+
else
|
122
|
+
@reflection.klass.count(:conditions => @counter_sql)
|
123
|
+
end
|
124
|
+
|
125
|
+
@target = [] and loaded if count == 0
|
126
|
+
|
127
|
+
if @reflection.options[:limit]
|
128
|
+
count = [ @reflection.options[:limit], count ].min
|
129
|
+
end
|
130
|
+
|
131
|
+
return count
|
132
|
+
end
|
133
|
+
|
134
|
+
def has_cached_counter?
|
135
|
+
@owner.attribute_present?(cached_counter_attribute_name)
|
136
|
+
end
|
137
|
+
|
138
|
+
def cached_counter_attribute_name
|
139
|
+
"#{@reflection.name}_count"
|
140
|
+
end
|
141
|
+
|
142
|
+
def insert_record(record)
|
143
|
+
set_belongs_to_association_for(record)
|
144
|
+
record.save
|
145
|
+
end
|
146
|
+
|
147
|
+
def delete_records(records)
|
148
|
+
if @reflection.options[:dependent]
|
149
|
+
records.each { |r| r.destroy }
|
150
|
+
else
|
151
|
+
ids = quoted_record_ids(records)
|
152
|
+
@reflection.klass.update_all(
|
153
|
+
"#{@reflection.primary_key_name} = NULL",
|
154
|
+
"#{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
|
155
|
+
)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def target_obsolete?
|
160
|
+
false
|
161
|
+
end
|
162
|
+
|
163
|
+
def construct_sql
|
164
|
+
case
|
165
|
+
when @reflection.options[:finder_sql]
|
166
|
+
@finder_sql = interpolate_sql(@reflection.options[:finder_sql])
|
167
|
+
|
168
|
+
when @reflection.options[:as]
|
169
|
+
@finder_sql =
|
170
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
|
171
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
|
172
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
173
|
+
|
174
|
+
else
|
175
|
+
@finder_sql = "#{@reflection.klass.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}"
|
176
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
177
|
+
end
|
178
|
+
|
179
|
+
if @reflection.options[:counter_sql]
|
180
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
181
|
+
elsif @reflection.options[:finder_sql]
|
182
|
+
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
|
183
|
+
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
|
184
|
+
@counter_sql = interpolate_sql(@reflection.options[:counter_sql])
|
185
|
+
else
|
186
|
+
@counter_sql = @finder_sql
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module ArAssociations
|
3
|
+
class HasOneAssociation < BelongsToAssociation #:nodoc:
|
4
|
+
def initialize(owner, reflection)
|
5
|
+
super
|
6
|
+
construct_sql
|
7
|
+
end
|
8
|
+
|
9
|
+
def create(attributes = {}, replace_existing = true)
|
10
|
+
record = build(attributes, replace_existing)
|
11
|
+
record.save
|
12
|
+
record
|
13
|
+
end
|
14
|
+
|
15
|
+
def build(attributes = {}, replace_existing = true)
|
16
|
+
record = @reflection.klass.new(attributes)
|
17
|
+
|
18
|
+
if replace_existing
|
19
|
+
replace(record, true)
|
20
|
+
else
|
21
|
+
record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
|
22
|
+
self.target = record
|
23
|
+
end
|
24
|
+
|
25
|
+
record
|
26
|
+
end
|
27
|
+
|
28
|
+
def replace(obj, dont_save = false)
|
29
|
+
load_target
|
30
|
+
|
31
|
+
unless @target.nil?
|
32
|
+
if dependent? && !dont_save && @target != obj
|
33
|
+
@target.destroy unless @target.new_record?
|
34
|
+
@owner.clear_association_cache
|
35
|
+
else
|
36
|
+
@target[@reflection.primary_key_name] = nil
|
37
|
+
@target.save unless @owner.new_record? || @target.new_record?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if obj.nil?
|
42
|
+
@target = nil
|
43
|
+
else
|
44
|
+
raise_on_type_mismatch(obj)
|
45
|
+
set_belongs_to_association_for(obj)
|
46
|
+
@target = (AssociationProxy === obj ? obj.target : obj)
|
47
|
+
end
|
48
|
+
|
49
|
+
@loaded = true
|
50
|
+
|
51
|
+
unless @owner.new_record? or obj.nil? or dont_save
|
52
|
+
return (obj.save ? self : false)
|
53
|
+
else
|
54
|
+
return (obj.nil? ? nil : self)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def find_target
|
60
|
+
@reflection.klass.find(:first,
|
61
|
+
:conditions => @finder_sql,
|
62
|
+
:order => @reflection.options[:order],
|
63
|
+
:include => @reflection.options[:include]
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def construct_sql
|
68
|
+
case
|
69
|
+
when @reflection.options[:as]
|
70
|
+
@finder_sql =
|
71
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_id = #{@owner.quoted_id} AND " +
|
72
|
+
"#{@reflection.klass.table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}"
|
73
|
+
else
|
74
|
+
@finder_sql = "#{@reflection.table_name}.#{@reflection.primary_key_name} = #{@owner.quoted_id}"
|
75
|
+
end
|
76
|
+
@finder_sql << " AND (#{conditions})" if conditions
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|