ooor 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,9 +19,9 @@ Ooor.xtend('ir.module.module') do
19
19
  dependency_modules = []
20
20
  modules.select { |m| m.dependencies_id }.each do |mod|
21
21
  mod.dependencies_id.each do |dep|
22
- dep_module = IrModuleModule.find(:first,
23
- :domain => [['name', '=', dep.name]],
24
- :fields => ['id', 'state', 'dependencies_id'])
22
+ dep_module = self.find(:first,
23
+ :domain => [['name', '=', dep.name]],
24
+ :fields => ['id', 'state', 'dependencies_id'])
25
25
  if dep_module.nil?
26
26
  raise RuntimeError, "#{dep.name} not found"
27
27
  end
@@ -32,66 +32,13 @@ Ooor.xtend('ir.module.module') do
32
32
  dependency_modules.uniq { |m| m.id }
33
33
  end
34
34
 
35
- ##########################################################################
36
- # Run the upgrade wizard in order to install the required
37
- # modules. Upgrade installed modules as well.
38
- # Input :
39
- # - modules : A [] of valid IrModuleModule instance
40
- # Return
41
- # - True
42
- # Usage Example:
43
- # res = IrModuleModule.install_modules(@openerp, modules)
44
- def self.install_modules(modules, dependencies=false)
45
- res = true
46
- if dependencies
47
- dependency_modules = get_dependencies(modules)
48
- modules.concat(dependency_modules) if dependency_modules
49
- end
50
- modules_toinstall_ids = []
51
- modules_toupgrade_ids = []
52
- # If not installed, do it. Otherwise update it
53
- modules.each do |m|
54
- if m.state == 'uninstalled'
55
- m.state = 'to install'
56
- m.save
57
- modules_toinstall_ids << m.id
58
- elsif m.state == 'installed'
59
- m.state = 'to upgrade'
60
- m.save
61
- modules_toupgrade_ids << m.id
62
- elsif m.state == 'to install'
63
- modules_toinstall_ids << m.id
64
- elsif m.state == 'to upgrade'
65
- modules_toupgrade_ids << m.id
66
- end
67
- end
68
- #First installed required modules, then upgrade the others
69
- upgrade = BaseModuleUpgrade.create()
70
- upgrade.upgrade_module()
71
- # IrModuleModule.button_install(modules_toinstall_ids)
72
- # IrModuleModule.button_upgrade(modules_toupgrade_ids)
73
-
74
- if res
75
- return true
76
- else
77
- raise "!!! --- HELPER ERROR : install_modules was unable to install needed modules.."
78
- end
79
- openerp.load_models() # reload in order to have model Classes for modules installed
80
- end
81
-
82
- def print_uml
83
- l = IrModelData.find(:all, :domain => {:model=>"ir.model", :module=>name})
84
- model_names = []
85
- l.each {|i| model_names << i.name.gsub('_', '.').gsub(/^model.report/, '').gsub(/^model./, '')}
86
- classes = []
87
- model_names.each {|i| begin classes << Object.const_get(IrModel.class_name_from_model_key i); rescue; end}
88
- classes.reject! {|m| m.openerp_model.index("report")} #NOTE we would need a more robust test
89
- begin
90
- classes.reject! {|m| IrModel.read(m.openerp_id, ['osv_memory'])['osv_memory']}
91
- rescue
35
+ def self.install_modules(modules)
36
+ modules = modules.map { |name| self.find(:first, domain: {name: name})}
37
+ modules.each do |mod|
38
+ mod.button_install unless mod.state == "installed"
92
39
  end
93
- classes.reject! {|m| m.openerp_model == "res.company"} if classes.size > 10
94
- OoorDoc::UML.print_uml(classes, {:file_name => "#{name}_uml"})
40
+ wizard = BaseModuleUpgrade.create
41
+ wizard.upgrade_module
95
42
  end
96
43
 
97
44
  def print_dependency_graph
@@ -122,7 +69,6 @@ Ooor.xtend('ir.module.module') do
122
69
  system("tred < #{self.name}-pre.dot > #{self.name}.dot")
123
70
  cmd_line2 = "dot -Tcmapx -o#{self.name}.map -Tpng -o#{self.name}.png #{self.name}.dot"
124
71
  system(cmd_line2)
125
-
126
72
  end
127
73
 
128
74
  end
@@ -68,24 +68,6 @@ module Ooor
68
68
  self.class.__send__(:split_options, options)
69
69
  end
70
70
 
71
- def method_missing(method_symbol, *arguments) #:nodoc:
72
- method_name = method_symbol.to_s
73
-
74
- if method_name =~ /(=|\?)$/
75
- case $1
76
- when "="
77
- attributes[$`] = arguments.first
78
- when "?"
79
- attributes[$`]
80
- end
81
- else
82
- return attributes[method_name] if attributes.include?(method_name)
83
- # not set right now but we know about it
84
- return nil if known_attributes.include?(method_name)
85
- super
86
- end
87
- end
88
-
89
71
  include ActiveModel::Conversion
90
72
  include ActiveModel::Serializers::JSON
91
73
  include ActiveModel::Serializers::Xml
@@ -10,7 +10,7 @@ module Ooor
10
10
  namespace = self.parents.detect do |n|
11
11
  n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
12
12
  end
13
- ActiveModel::Name.new(self, namespace, self.description).tap do |r|
13
+ ActiveModel::Name.new(self, namespace, self.description || self.openerp_model).tap do |r|
14
14
  def r.param_key
15
15
  @klass.openerp_model.gsub('.', '_')
16
16
  end
@@ -52,7 +52,7 @@ module Ooor
52
52
  def alias(context={})
53
53
  # NOTE in v8, see if we can use ModelConvert here https://github.com/akretion/openerp-addons/blob/trunk-website-al/website/models/ir_http.py#L126
54
54
  if connection.config[:aliases]
55
- lang = context['lang'] || connection.config[:aliases][connection.config['lang'] || 'en_US']
55
+ lang = context['lang'] || connection.config['lang'] || 'en_US'
56
56
  if alias_data = connection.config[:aliases][lang]
57
57
  alias_data.select{|key, value| value == openerp_model }.keys[0] || openerp_model
58
58
  else
@@ -0,0 +1,140 @@
1
+ # OOOR: OpenObject On Ruby
2
+ # Copyright (C) 2009-2014 Akretion LTDA (<http://www.akretion.com>).
3
+ # Author: Raphaël Valyi
4
+ # Licensed under the MIT license, see MIT-LICENSE file
5
+
6
+ require 'active_support/core_ext/hash/indifferent_access'
7
+ require 'active_model/attribute_methods'
8
+ require 'active_model/dirty'
9
+ require 'ooor/errors'
10
+
11
+ module Ooor
12
+
13
+ # the base class for proxies to OpenERP objects
14
+ module Persistence
15
+
16
+ def load(attributes, remove_root=false, persisted=false)#an attribute might actually be a association too, will be determined here
17
+ self.class.reload_fields_definition(false, object_session)
18
+ raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash)
19
+ @prefix_options, attributes = split_options(attributes)
20
+ @associations ||= {}
21
+ @attributes ||= {}
22
+ @loaded_associations = {}
23
+ attributes.each do |key, value|
24
+ self.send "#{key}=".to_sym, value if self.respond_to?("#{key}=".to_sym)
25
+ end
26
+ self
27
+ end
28
+
29
+ #takes care of reading OpenERP default field values.
30
+ def initialize(attributes = {}, default_get_list=false, context={}, persisted=false)
31
+ @attributes = {}
32
+ @prefix_options = {}
33
+ @ir_model_data_id = attributes.delete(:ir_model_data_id)
34
+ @object_session = {}
35
+ @object_session = HashWithIndifferentAccess.new(context)
36
+ @persisted = persisted
37
+ self.class.reload_fields_definition(false, @object_session)
38
+ if default_get_list == []
39
+ load(attributes)
40
+ else
41
+ load_with_defaults(attributes, default_get_list)
42
+ end.tap do
43
+ if id
44
+ @previously_changed = ActiveSupport::HashWithIndifferentAccess.new # see ActiveModel::Dirty reset_changes
45
+ @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
46
+ end
47
+ end
48
+ end
49
+
50
+ # Saves (+create+) or \updates (+write+) a resource. Delegates to +create+ if the object is \new,
51
+ # +update+ if it exists.
52
+ def save(context={}, reload=true)
53
+ create_or_update(context, reload)
54
+ end
55
+
56
+ def create_or_update(context={}, reload=true)
57
+ run_callbacks :save do
58
+ new? ? create_record(context, reload) : update_record(context, reload)
59
+ end
60
+ rescue ValidationError => e
61
+ e.extract_validation_error!(errors)
62
+ return false
63
+ end
64
+
65
+ # Create (i.e., \save to OpenERP service) the \new resource.
66
+ def create(context={}, reload=true)
67
+ create_or_update(context, reload)
68
+ end
69
+
70
+ def create_record(context={}, reload=true)
71
+ run_callbacks :create do
72
+ self.id = rpc_execute('create', to_openerp_hash, context)
73
+ if @ir_model_data_id
74
+ IrModelData.create(model: self.class.openerp_model,
75
+ 'module' => @ir_model_data_id[0],
76
+ 'name' => @ir_model_data_id[1],
77
+ 'res_id' => self.id)
78
+ end
79
+ @persisted = true
80
+ reload_fields(context) if reload
81
+ end
82
+ end
83
+
84
+ def update_attributes(attributes, context={}, reload=true)
85
+ load(attributes, false) && save(context, reload)
86
+ end
87
+
88
+ # Update the resource on the remote service.
89
+ def update(context={}, reload=true, keys=nil)
90
+ create_or_update(context, reload, keys)
91
+ end
92
+
93
+ def update_record(context={}, reload=true)
94
+ run_callbacks :update do
95
+ rpc_execute('write', [self.id], to_openerp_hash, context)
96
+ reload_fields(context) if reload
97
+ @persisted = true
98
+ end
99
+ end
100
+
101
+ #Deletes the record in OpenERP and freezes this instance to reflect that no changes should be made (since they can’t be persisted).
102
+ def destroy(context={})
103
+ run_callbacks :destroy do
104
+ rpc_execute('unlink', [self.id], context)
105
+ @destroyed = true
106
+ freeze
107
+ end
108
+ end
109
+
110
+ #OpenERP copy method, load persisted copied Object
111
+ def copy(defaults={}, context={})
112
+ self.class.find(rpc_execute('copy', self.id, defaults, context), context: context)
113
+ end
114
+
115
+ private
116
+
117
+ def load_with_defaults(attributes, default_get_list)
118
+ defaults = rpc_execute("default_get", default_get_list || self.class.fields.keys + self.class.associations_keys, object_session.dup)
119
+ attributes = HashWithIndifferentAccess.new(defaults.merge(attributes.reject {|k, v| v.blank? }))
120
+ load(attributes)
121
+ end
122
+
123
+ def load_on_change_result(result, field_name, field_value)
124
+ if result["warning"]
125
+ self.class.logger.info result["warning"]["title"]
126
+ self.class.logger.info result["warning"]["message"]
127
+ end
128
+ attrs = @attributes.merge(field_name => field_value)
129
+ attrs.merge!(result["value"])
130
+ load(attrs)
131
+ end
132
+
133
+ def reload_fields(context)
134
+ record = self.class.find(self.id, context: context)
135
+ load(record.attributes.merge(record.associations))
136
+ @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new # see ActiveModel::Dirty
137
+ end
138
+
139
+ end
140
+ end
@@ -1,5 +1,6 @@
1
1
  require "rails/railtie"
2
2
  require "ooor/rack"
3
+ require "yaml"
3
4
 
4
5
  module Ooor
5
6
  class Railtie < Rails::Railtie
@@ -29,7 +30,7 @@ module Ooor
29
30
 
30
31
  def load_config(config_file=nil, env=nil)
31
32
  config_file ||= defined?(Rails.root) && "#{Rails.root}/config/ooor.yml" || 'ooor.yml'
32
- @config = HashWithIndifferentAccess.new(YAML.load_file(config_file)[env || 'development'])
33
+ @config = HashWithIndifferentAccess.new(::YAML.load_file(config_file)[env || 'development'])
33
34
  rescue SystemCallError
34
35
  Ooor.logger.error """failed to load OOOR yaml configuration file.
35
36
  make sure your app has a #{config_file} file correctly set up
@@ -2,7 +2,7 @@ require 'active_support/core_ext/class/attribute'
2
2
  require 'active_support/core_ext/object/inclusion'
3
3
 
4
4
  # NOTE this is a scoped copy of ActiveRecord reflection.rb
5
- # the few necessary hacks are explicited with a FIXME
5
+ # the few necessary hacks are explicited with a FIXME or a NOTE
6
6
  # an addition Ooor specific reflection module completes this one explicitely
7
7
  module Ooor
8
8
  # = Active Record Reflection
@@ -23,18 +23,7 @@ module Ooor
23
23
  # MacroReflection class has info for AggregateReflection and AssociationReflection
24
24
  # classes.
25
25
  module ClassMethods
26
- def create_reflection(macro, name, options, active_record)
27
- case macro
28
- when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
29
- klass = options[:through] ? ThroughReflection : AssociationReflection
30
- reflection = klass.new(macro, name, options, active_record)
31
- when :composed_of
32
- reflection = AggregateReflection.new(macro, name, options, active_record)
33
- end
34
-
35
- self.reflections = self.reflections.merge(name => reflection)
36
- reflection
37
- end
26
+ #def create_reflection(macro, name, options, active_record) #NOTE overriden in Ooor
38
27
 
39
28
  # Returns an array of AggregateReflection objects for all the aggregations in the class.
40
29
  def reflect_on_all_aggregations
@@ -64,14 +53,7 @@ module Ooor
64
53
  macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
65
54
  end
66
55
 
67
- # Returns the AssociationReflection object for the +association+ (use the symbol).
68
- #
69
- # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
70
- # Invoice.reflect_on_association(:line_items).macro # returns :has_many
71
- #
72
- def reflect_on_association(association)
73
- reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
74
- end
56
+ # def reflect_on_association(association) # NOTE overriden in Ooor
75
57
 
76
58
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
77
59
  def reflect_on_all_autosave_associations
@@ -118,9 +100,9 @@ module Ooor
118
100
  #
119
101
  # <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money class
120
102
  # <tt>has_many :clients</tt> returns the Client class
121
- def klass
122
- @klass ||= class_name.constantize
123
- end
103
+ # def klass #NOTE overriden in Ooor
104
+ # @klass ||= class_name.constantize
105
+ # end
124
106
 
125
107
  # Returns the class name for the macro.
126
108
  #
@@ -140,9 +122,9 @@ module Ooor
140
122
  active_record == other_aggregation.active_record
141
123
  end
142
124
 
143
- def sanitized_conditions #:nodoc:
144
- @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
145
- end
125
+ # def sanitized_conditions #:nodoc: #NOTE not applicable in Ooor
126
+ # @sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
127
+ # end
146
128
 
147
129
  private
148
130
  def derive_class_name
@@ -158,380 +140,10 @@ module Ooor
158
140
 
159
141
  # Holds all the meta-data about an association as it was specified in the
160
142
  # Active Record class.
161
- class AssociationReflection < MacroReflection #:nodoc:
162
- # Returns the target association's class.
163
- #
164
- # class Author < ActiveRecord::Base
165
- # has_many :books
166
- # end
167
- #
168
- # Author.reflect_on_association(:books).klass
169
- # # => Book
170
- #
171
- # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
172
- # a new association object. Use +build_association+ or +create_association+
173
- # instead. This allows plugins to hook into association object creation.
174
- def klass
175
- @klass ||= active_record.send(:compute_type, class_name)
176
- end
177
-
178
- def initialize(macro, name, options, active_record)
179
- super
180
- @collection = macro.in?([:has_many, :has_and_belongs_to_many])
181
- end
182
-
183
- # Returns a new, unsaved instance of the associated class. +options+ will
184
- # be passed to the class's constructor.
185
- def build_association(*options, &block)
186
- klass.new(*options, &block)
187
- end
188
-
189
- def table_name
190
- @table_name ||= klass.table_name
191
- end
192
-
193
- def quoted_table_name
194
- @quoted_table_name ||= klass.quoted_table_name
195
- end
196
-
197
- def foreign_key
198
- @foreign_key ||= options[:foreign_key] || derive_foreign_key
199
- end
200
-
201
- def foreign_type
202
- @foreign_type ||= options[:foreign_type] || "#{name}_type"
203
- end
204
-
205
- def type
206
- @type ||= options[:as] && "#{options[:as]}_type"
207
- end
208
-
209
- def primary_key_column
210
- @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
211
- end
212
-
213
- def association_foreign_key
214
- @association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
215
- end
216
-
217
- # klass option is necessary to support loading polymorphic associations
218
- def association_primary_key(klass = nil)
219
- options[:primary_key] || primary_key(klass || self.klass)
220
- end
221
-
222
- def active_record_primary_key
223
- @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
224
- end
225
-
226
- def counter_cache_column
227
- if options[:counter_cache] == true
228
- "#{active_record.name.demodulize.underscore.pluralize}_count"
229
- elsif options[:counter_cache]
230
- options[:counter_cache].to_s
231
- end
232
- end
233
-
234
- def columns(tbl_name, log_msg)
235
- @columns ||= klass.connection.columns(tbl_name, log_msg)
236
- end
237
-
238
- def reset_column_information
239
- @columns = nil
240
- end
241
-
242
- def check_validity!
243
- check_validity_of_inverse!
244
- end
245
-
246
- def check_validity_of_inverse!
247
- unless options[:polymorphic]
248
- if has_inverse? && inverse_of.nil?
249
- raise InverseOfAssociationNotFoundError.new(self)
250
- end
251
- end
252
- end
253
-
254
- def through_reflection
255
- nil
256
- end
257
-
258
- def source_reflection
259
- nil
260
- end
261
-
262
- # A chain of reflections from this one back to the owner. For more see the explanation in
263
- # ThroughReflection.
264
- def chain
265
- [self]
266
- end
267
-
268
- def nested?
269
- false
270
- end
271
-
272
- # An array of arrays of conditions. Each item in the outside array corresponds to a reflection
273
- # in the #chain. The inside arrays are simply conditions (and each condition may itself be
274
- # a hash, array, arel predicate, etc...)
275
- def conditions
276
- [[options[:conditions]].compact]
277
- end
278
-
279
- alias :source_macro :macro
280
-
281
- def has_inverse?
282
- @options[:inverse_of]
283
- end
284
-
285
- def inverse_of
286
- if has_inverse?
287
- @inverse_of ||= klass.reflect_on_association(options[:inverse_of])
288
- end
289
- end
290
-
291
- def polymorphic_inverse_of(associated_class)
292
- if has_inverse?
293
- if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
294
- inverse_relationship
295
- else
296
- raise InverseOfAssociationNotFoundError.new(self, associated_class)
297
- end
298
- end
299
- end
300
-
301
- # Returns whether or not this association reflection is for a collection
302
- # association. Returns +true+ if the +macro+ is either +has_many+ or
303
- # +has_and_belongs_to_many+, +false+ otherwise.
304
- def collection?
305
- @collection
306
- end
307
-
308
- # Returns whether or not the association should be validated as part of
309
- # the parent's validation.
310
- #
311
- # Unless you explicitly disable validation with
312
- # <tt>:validate => false</tt>, validation will take place when:
313
- #
314
- # * you explicitly enable validation; <tt>:validate => true</tt>
315
- # * you use autosave; <tt>:autosave => true</tt>
316
- # * the association is a +has_many+ association
317
- def validate?
318
- !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
319
- end
320
-
321
- # Returns +true+ if +self+ is a +belongs_to+ reflection.
322
- def belongs_to?
323
- macro == :belongs_to
324
- end
325
-
326
- def association_class
327
- case macro
328
- when :belongs_to
329
- if options[:polymorphic]
330
- Associations::BelongsToPolymorphicAssociation
331
- else
332
- Associations::BelongsToAssociation
333
- end
334
- when :has_and_belongs_to_many
335
- Associations::HasAndBelongsToManyAssociation
336
- when :has_many
337
- if options[:through]
338
- Associations::HasManyThroughAssociation
339
- else
340
- Associations::HasManyAssociation
341
- end
342
- when :has_one
343
- if options[:through]
344
- Associations::HasOneThroughAssociation
345
- else
346
- Associations::HasOneAssociation
347
- end
348
- end
349
- end
350
-
351
- private
352
- def derive_class_name
353
- class_name = name.to_s.camelize
354
- class_name = class_name.singularize if collection?
355
- class_name
356
- end
357
-
358
- def derive_foreign_key
359
- if belongs_to?
360
- "#{name}_id"
361
- elsif options[:as]
362
- "#{options[:as]}_id"
363
- else
364
- active_record.name.foreign_key
365
- end
366
- end
367
-
368
- def primary_key(klass)
369
- klass.primary_key || raise(UnknownPrimaryKey.new(klass))
370
- end
371
- end
143
+ #class AssociationReflection < MacroReflection #:nodoc: #NOTE totally overriden in Ooor
372
144
 
373
145
  # Holds all the meta-data about a :through association as it was specified
374
146
  # in the Active Record class.
375
- class ThroughReflection < AssociationReflection #:nodoc:
376
- # delegate :foreign_key, :foreign_type, :association_foreign_key, #FIXME hacked for OOOR
377
- # :active_record_primary_key, :type, :to => :source_reflection
378
-
379
- # Gets the source of the through reflection. It checks both a singularized
380
- # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
381
- #
382
- # class Post < ActiveRecord::Base
383
- # has_many :taggings
384
- # has_many :tags, :through => :taggings
385
- # end
386
- #
387
- def source_reflection
388
- @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
389
- end
390
-
391
- # Returns the AssociationReflection object specified in the <tt>:through</tt> option
392
- # of a HasManyThrough or HasOneThrough association.
393
- #
394
- # class Post < ActiveRecord::Base
395
- # has_many :taggings
396
- # has_many :tags, :through => :taggings
397
- # end
398
- #
399
- # tags_reflection = Post.reflect_on_association(:tags)
400
- # taggings_reflection = tags_reflection.through_reflection
401
- #
402
- def through_reflection
403
- @through_reflection ||= active_record.reflect_on_association(options[:through])
404
- end
405
-
406
- # Returns an array of reflections which are involved in this association. Each item in the
407
- # array corresponds to a table which will be part of the query for this association.
408
- #
409
- # The chain is built by recursively calling #chain on the source reflection and the through
410
- # reflection. The base case for the recursion is a normal association, which just returns
411
- # [self] as its #chain.
412
- def chain
413
- @chain ||= begin
414
- chain = source_reflection.chain + through_reflection.chain
415
- chain[0] = self # Use self so we don't lose the information from :source_type
416
- chain
417
- end
418
- end
419
-
420
- # Consider the following example:
421
- #
422
- # class Person
423
- # has_many :articles
424
- # has_many :comment_tags, :through => :articles
425
- # end
426
- #
427
- # class Article
428
- # has_many :comments
429
- # has_many :comment_tags, :through => :comments, :source => :tags
430
- # end
431
- #
432
- # class Comment
433
- # has_many :tags
434
- # end
435
- #
436
- # There may be conditions on Person.comment_tags, Article.comment_tags and/or Comment.tags,
437
- # but only Comment.tags will be represented in the #chain. So this method creates an array
438
- # of conditions corresponding to the chain. Each item in the #conditions array corresponds
439
- # to an item in the #chain, and is itself an array of conditions from an arbitrary number
440
- # of relevant reflections, plus any :source_type or polymorphic :as constraints.
441
- def conditions
442
- @conditions ||= begin
443
- conditions = source_reflection.conditions.map { |c| c.dup }
444
-
445
- # Add to it the conditions from this reflection if necessary.
446
- conditions.first << options[:conditions] if options[:conditions]
447
-
448
- through_conditions = through_reflection.conditions
449
-
450
- if options[:source_type]
451
- through_conditions.first << { foreign_type => options[:source_type] }
452
- end
453
-
454
- # Recursively fill out the rest of the array from the through reflection
455
- conditions += through_conditions
456
-
457
- # And return
458
- conditions
459
- end
460
- end
461
-
462
- # The macro used by the source association
463
- def source_macro
464
- source_reflection.source_macro
465
- end
466
-
467
- # A through association is nested if there would be more than one join table
468
- def nested?
469
- chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
470
- end
471
-
472
- # We want to use the klass from this reflection, rather than just delegate straight to
473
- # the source_reflection, because the source_reflection may be polymorphic. We still
474
- # need to respect the source_reflection's :primary_key option, though.
475
- def association_primary_key(klass = nil)
476
- # Get the "actual" source reflection if the immediate source reflection has a
477
- # source reflection itself
478
- source_reflection = self.source_reflection
479
- while source_reflection.source_reflection
480
- source_reflection = source_reflection.source_reflection
481
- end
482
-
483
- source_reflection.options[:primary_key] || primary_key(klass || self.klass)
484
- end
485
-
486
- # Gets an array of possible <tt>:through</tt> source reflection names:
487
- #
488
- # [:singularized, :pluralized]
489
- #
490
- def source_reflection_names
491
- @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
492
- end
493
-
494
- def source_options
495
- source_reflection.options
496
- end
497
-
498
- def through_options
499
- through_reflection.options
500
- end
501
-
502
- def check_validity!
503
- if through_reflection.nil?
504
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
505
- end
506
-
507
- if through_reflection.options[:polymorphic]
508
- raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
509
- end
510
-
511
- if source_reflection.nil?
512
- raise HasManyThroughSourceAssociationNotFoundError.new(self)
513
- end
514
-
515
- if options[:source_type] && source_reflection.options[:polymorphic].nil?
516
- raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
517
- end
518
-
519
- if source_reflection.options[:polymorphic] && options[:source_type].nil?
520
- raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
521
- end
522
-
523
- if macro == :has_one && through_reflection.collection?
524
- raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
525
- end
526
-
527
- check_validity_of_inverse!
528
- end
529
-
530
- private
531
- def derive_class_name
532
- # get the class_name of the belongs_to association of the through reflection
533
- options[:source_type] || source_reflection.class_name
534
- end
535
- end
147
+ #class ThroughReflection < AssociationReflection #:nodoc: #NOTE commented out because not used in Ooor
536
148
  end
537
149
  end