ooor 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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