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,194 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module BrAssociations
|
3
|
+
module CachedItemProxy #:nodoc:
|
4
|
+
|
5
|
+
CACHE_ATTRIBUTE = "attribute:associations_cache"
|
6
|
+
|
7
|
+
attr_reader :reflection
|
8
|
+
alias_method :proxy_respond_to?, :respond_to?
|
9
|
+
alias_method :proxy_extend, :extend
|
10
|
+
# delegate :to_param, :to => :proxy_target
|
11
|
+
# instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_)/ }
|
12
|
+
|
13
|
+
def proxy_cache
|
14
|
+
@owner[CACHE_ATTRIBUTE] ||= {}
|
15
|
+
@owner[CACHE_ATTRIBUTE]["#{@reflection.klass.name}:#{id}"] ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def proxy_owner
|
19
|
+
@owner
|
20
|
+
end
|
21
|
+
|
22
|
+
def proxy_reflection
|
23
|
+
@reflection
|
24
|
+
end
|
25
|
+
|
26
|
+
def proxy_target
|
27
|
+
@target
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to?(symbol, include_priv = false)
|
31
|
+
proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Explicitly proxy === because the instance method removal above
|
35
|
+
# doesn't catch it.
|
36
|
+
def ===(other)
|
37
|
+
load_target
|
38
|
+
other === @target
|
39
|
+
end
|
40
|
+
|
41
|
+
def aliased_table_name
|
42
|
+
@reflection.klass.table_name
|
43
|
+
end
|
44
|
+
|
45
|
+
def reset
|
46
|
+
@loaded = false
|
47
|
+
@target = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def reload
|
51
|
+
reset
|
52
|
+
load_target
|
53
|
+
end
|
54
|
+
|
55
|
+
def loaded?
|
56
|
+
@loaded
|
57
|
+
end
|
58
|
+
|
59
|
+
def loaded
|
60
|
+
@loaded = true
|
61
|
+
end
|
62
|
+
|
63
|
+
def target
|
64
|
+
@target
|
65
|
+
end
|
66
|
+
|
67
|
+
def target=(target)
|
68
|
+
@target = target
|
69
|
+
loaded
|
70
|
+
end
|
71
|
+
|
72
|
+
# # Returns the contents of the record as a nicely formatted string.
|
73
|
+
# def inspect
|
74
|
+
# if loaded?
|
75
|
+
# @target.inspect
|
76
|
+
# else
|
77
|
+
# attributes_as_nice_string = @reflection.options[:cache].collect { |name|
|
78
|
+
# column = @owner.column_for_attribute("attribute:#{name}")
|
79
|
+
# "#{name}: #{column.type_cast(proxy_cache[name])}" if column
|
80
|
+
# }.compact.join(", ")
|
81
|
+
# "#<Cached#{@reflection.klass} #{attributes_as_nice_string}>"
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
|
85
|
+
# def is_a?(klass)
|
86
|
+
# @reflection.klass <= klass
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# def kind_of?(klass)
|
90
|
+
# @reflection.klass <= klass
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# def to_param
|
94
|
+
# @id
|
95
|
+
# end
|
96
|
+
|
97
|
+
# protected
|
98
|
+
# def dependent?
|
99
|
+
# @reflection.options[:dependent] || false
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# def quoted_record_ids(records)
|
103
|
+
# records.map { |record| record.quoted_id }.join(',')
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
## def interpolate_sql_options!(options, *keys)
|
107
|
+
## keys.each { |key| options[key] &&= interpolate_sql(options[key]) }
|
108
|
+
## end
|
109
|
+
##
|
110
|
+
## def interpolate_sql(sql, record = nil)
|
111
|
+
## @owner.send(:interpolate_sql, sql, record)
|
112
|
+
## end
|
113
|
+
##
|
114
|
+
## def sanitize_sql(sql)
|
115
|
+
## @reflection.klass.send(:sanitize_sql, sql)
|
116
|
+
## end
|
117
|
+
#
|
118
|
+
# def extract_options_from_args!(args)
|
119
|
+
# @owner.send(:extract_options_from_args!, args)
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# def set_belongs_to_association_for(record)
|
123
|
+
# if @reflection.options[:as]
|
124
|
+
# record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
|
125
|
+
# record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
|
126
|
+
# else
|
127
|
+
# record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# def merge_options_from_reflection!(options)
|
132
|
+
# options.reverse_merge!(
|
133
|
+
# :group => @reflection.options[:group],
|
134
|
+
# :limit => @reflection.options[:limit],
|
135
|
+
# :offset => @reflection.options[:offset],
|
136
|
+
# :joins => @reflection.options[:joins],
|
137
|
+
# :include => @reflection.options[:include],
|
138
|
+
# :select => @reflection.options[:select]
|
139
|
+
# )
|
140
|
+
# end
|
141
|
+
|
142
|
+
# private
|
143
|
+
# def method_missing(method_id, *args, &block)
|
144
|
+
# if !loaded? and @reflection.options[:cache].include?(method_id)
|
145
|
+
# # FIXME: shouldn't be hard coded
|
146
|
+
# column = @owner.column_for_attribute("attribute:#{method_id}")
|
147
|
+
# if column
|
148
|
+
# if proxy_cache.has_key?(method_id)
|
149
|
+
# column.type_cast(proxy_cache[method_id])
|
150
|
+
# elsif load_target
|
151
|
+
# proxy_cache[method_id] = @target.send(method_id, *args, &block)
|
152
|
+
# end
|
153
|
+
# else
|
154
|
+
# @target.send(method_id, *args, &block)
|
155
|
+
# end
|
156
|
+
# elsif load_target
|
157
|
+
# value = @target.send(method_id, *args, &block)
|
158
|
+
# proxy_cache[method_id] = value if @reflection.options[:cache].include?(method_id)
|
159
|
+
# value
|
160
|
+
# end
|
161
|
+
# end
|
162
|
+
|
163
|
+
def load_target
|
164
|
+
return nil unless defined?(@loaded)
|
165
|
+
|
166
|
+
if !loaded? and (!@owner.new_record? || foreign_key_present)
|
167
|
+
@target = find_target
|
168
|
+
end
|
169
|
+
|
170
|
+
@loaded = true
|
171
|
+
@target
|
172
|
+
rescue BigRecord::RecordNotFound
|
173
|
+
reset
|
174
|
+
end
|
175
|
+
|
176
|
+
def find_target
|
177
|
+
@reflection.klass.find(self.id)
|
178
|
+
end
|
179
|
+
|
180
|
+
# # Can be overwritten by associations that might have the foreign key available for an association without
|
181
|
+
# # having the object itself (and still being a new record). Currently, only belongs_to present this scenario.
|
182
|
+
# def foreign_key_present
|
183
|
+
# false
|
184
|
+
# end
|
185
|
+
#
|
186
|
+
# def raise_on_type_mismatch(record)
|
187
|
+
# unless record.is_a?(@reflection.klass)
|
188
|
+
# raise BigRecordRecord::AssociationTypeMismatch, "#{@reflection.class_name} expected, got #{record.class}"
|
189
|
+
# end
|
190
|
+
# end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module BrAssociations
|
3
|
+
class CachedItemProxyFactory
|
4
|
+
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
def create(id, owner, reflection)
|
8
|
+
cache = owner[CachedItemProxy::CACHE_ATTRIBUTE]
|
9
|
+
cached_attributes = cache["#{reflection.klass.name}:#{id}"] if cache
|
10
|
+
cached_attributes ||= {}
|
11
|
+
cached_attributes["id"] = id
|
12
|
+
proxy = reflection.klass.instantiate(cached_attributes)
|
13
|
+
proxy.extend CachedItemProxy
|
14
|
+
proxy.instance_variable_set(:@owner, owner)
|
15
|
+
proxy.instance_variable_set(:@reflection, reflection)
|
16
|
+
proxy.reset
|
17
|
+
|
18
|
+
# Overload the cached methods
|
19
|
+
reflection.options[:cache].each do |attribute_name|
|
20
|
+
eval "def proxy.#{attribute_name}\n"+
|
21
|
+
" proxy_cache[\"#{attribute_name}\"] ||= super\n"+
|
22
|
+
"end"
|
23
|
+
end
|
24
|
+
|
25
|
+
proxy
|
26
|
+
end
|
27
|
+
|
28
|
+
# def extended_class(reflection)
|
29
|
+
# @extended_classes ||= {}
|
30
|
+
# @extended_classes[reflection.klass.name] ||= create_extended_class(reflection)
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# def create_extended_class(reflection)
|
34
|
+
# extended_class = Class.new(reflection.klass)
|
35
|
+
# extended_class.class_eval do
|
36
|
+
# include CachedItemProxy
|
37
|
+
#
|
38
|
+
# attr_reader :reflection
|
39
|
+
# alias_method :proxy_respond_to?, :respond_to?
|
40
|
+
# alias_method :proxy_extend, :extend
|
41
|
+
## delegate :to_param, :to => :proxy_target
|
42
|
+
#
|
43
|
+
# # Overload the methods
|
44
|
+
# instance_methods.each do |m|
|
45
|
+
# if reflection.options[:cache].include?(m.to_sym)
|
46
|
+
# define_method m do
|
47
|
+
# proxy_cache[m.to_s] ||= super
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# def class
|
53
|
+
# reflection.klass
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# end
|
57
|
+
# extended_class
|
58
|
+
# end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module BrAssociations
|
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
|
+
# Generate the sql query. The join table has to be in mysql.
|
43
|
+
conditions = "#{@finder_sql}"
|
44
|
+
|
45
|
+
if sanitized_conditions = ActiveRecord::Base.send(:sanitize_sql, options[:conditions])
|
46
|
+
conditions << " AND (#{sanitized_conditions})"
|
47
|
+
end
|
48
|
+
|
49
|
+
if options[:order] && @reflection.options[:order]
|
50
|
+
order = "#{options[:order]}, #{@reflection.options[:order]}"
|
51
|
+
elsif @reflection.options[:order]
|
52
|
+
order = @reflection.options[:order]
|
53
|
+
end
|
54
|
+
|
55
|
+
query = "SELECT #{@reflection.association_foreign_key} FROM #{@reflection.options[:join_table]} WHERE #{conditions} #{order};"
|
56
|
+
|
57
|
+
# Execute the query
|
58
|
+
ids = []
|
59
|
+
ActiveRecord::Base.connection.execute(query).each do |result|
|
60
|
+
ids << result.first
|
61
|
+
end
|
62
|
+
|
63
|
+
# Find the big_record entries by id. Find them one at a time because duplicate entries must appear in
|
64
|
+
# the result set if it's the case. The default find by ids in big_record does the same thing anyway...
|
65
|
+
ids.collect{|id| @reflection.klass.find(id)}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
def count_records
|
71
|
+
load_target.size
|
72
|
+
end
|
73
|
+
|
74
|
+
def insert_record(record, force=true)
|
75
|
+
if record.new_record?
|
76
|
+
if force
|
77
|
+
record.save!
|
78
|
+
else
|
79
|
+
return false unless record.save
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if @reflection.options[:insert_sql]
|
84
|
+
ar_connection.execute(interpolate_sql(@reflection.options[:insert_sql], record))
|
85
|
+
else
|
86
|
+
columns = ar_connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns")
|
87
|
+
|
88
|
+
attributes = columns.inject({}) do |attributes, column|
|
89
|
+
case column.name
|
90
|
+
when @reflection.primary_key_name
|
91
|
+
attributes[column.name] = @owner.quoted_id
|
92
|
+
when @reflection.association_foreign_key
|
93
|
+
attributes[column.name] = record.quoted_id
|
94
|
+
else
|
95
|
+
if record.attributes.has_key?(column.name)
|
96
|
+
value = @owner.send(:quote_value, record[column.name], column)
|
97
|
+
attributes[column.name] = value unless value.nil?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
attributes
|
101
|
+
end
|
102
|
+
|
103
|
+
sql =
|
104
|
+
"INSERT INTO #{@reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " +
|
105
|
+
"VALUES (#{attributes.values.join(', ')})"
|
106
|
+
|
107
|
+
ar_connection.execute(sql)
|
108
|
+
end
|
109
|
+
|
110
|
+
return true
|
111
|
+
end
|
112
|
+
|
113
|
+
def delete_records(records)
|
114
|
+
if sql = @reflection.options[:delete_sql]
|
115
|
+
records.each { |record| ar_connection.execute(interpolate_sql(sql, record)) }
|
116
|
+
else
|
117
|
+
ids = quoted_record_ids(records)
|
118
|
+
sql = "DELETE FROM #{@reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{@owner.quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})"
|
119
|
+
ar_connection.execute(sql)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def construct_sql
|
124
|
+
# interpolate_sql_options!(@reflection.options, :finder_sql)
|
125
|
+
|
126
|
+
if @reflection.options[:finder_sql]
|
127
|
+
@finder_sql = @reflection.options[:finder_sql]
|
128
|
+
else
|
129
|
+
@finder_sql = "#{@reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
|
130
|
+
# @finder_sql << " AND (#{conditions})" if conditions
|
131
|
+
end
|
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 && ar_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
|
+
|
163
|
+
def ar_connection
|
164
|
+
(@owner.class < ActiveRecord::Base) ? @owner.connection : ActiveRecord::Base.connection
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module BigRecord
|
2
|
+
module BrAssociations
|
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
|