| | 377 | |
|---|
| | 378 | # Alternative access methodology using associations. Preserves standard find behavior. |
|---|
| | 379 | # <i>i.e. Globalize::DbTranslate.translation_method = :via_association</i> |
|---|
| | 380 | def translate_via_association(facets, options) |
|---|
| | 381 | class_eval <<-HERE |
|---|
| | 382 | @@facet_options = {} |
|---|
| | 383 | @@translation_reflections = [] |
|---|
| | 384 | @@translation_dyn_reflections = [] |
|---|
| | 385 | @@aliases = {} |
|---|
| | 386 | @@dyn_table_aliases = {} #Used for dynamic finders |
|---|
| | 387 | @@facets = facets |
|---|
| | 388 | has_many :translations, :class_name => 'Globalize::ModelTranslation', |
|---|
| | 389 | :conditions => ['globalize_translations.table_name = ?', table_name], |
|---|
| | 390 | :foreign_key => 'item_id', |
|---|
| | 391 | :dependent => :delete_all |
|---|
| | 392 | |
|---|
| | 393 | class << self |
|---|
| | 394 | alias_method :untranslated_find, :find |
|---|
| | 395 | def find(*args) |
|---|
| | 396 | options = args.last.is_a?(Hash) ? args.pop : {} |
|---|
| | 397 | if (options.has_key?(:untranslated) && options[:untranslated] == true) |
|---|
| | 398 | args << options |
|---|
| | 399 | untranslated_find(*args) |
|---|
| | 400 | else |
|---|
| | 401 | unless Locale.base? |
|---|
| | 402 | @@facets.each do |f| |
|---|
| | 403 | # Replaces table.untranslated_field or just untranslated_field with globalize_translations.text |
|---|
| | 404 | r = Regexp.new("(\\\\A|\\\\s)(#\{table_name\}.#\{f\}|#\{f\})(\\\\z|\\\\s)") |
|---|
| | 405 | options[:order].sub!(r, "\\\\1" + @@aliases[f] + "\\\\3") if options[:order] |
|---|
| | 406 | options[:group].sub!(r, "\\\\1" + @@aliases[f] + "\\\\3") if options[:group] |
|---|
| | 407 | options[:conditions].sub!(r, "\\\\1" + @@aliases[f] + "\\\\3") if options[:conditions].is_a?(String) |
|---|
| | 408 | end |
|---|
| | 409 | |
|---|
| | 410 | #If order is specified, only return results for current locale |
|---|
| | 411 | if options[:order] |
|---|
| | 412 | options[:conditions] ||= '' |
|---|
| | 413 | case options[:conditions] |
|---|
| | 414 | when String |
|---|
| | 415 | options[:conditions] << " AND" unless options[:conditions].empty? |
|---|
| | 416 | options[:conditions] << " globalize_translations.language_id = #\{Locale.language.id}" |
|---|
| | 417 | when Array |
|---|
| | 418 | options[:conditions].first << " AND" unless options[:conditions].first.empty? |
|---|
| | 419 | options[:conditions].first << " globalize_translations.language_id = #\{Locale.language.id}" |
|---|
| | 420 | else |
|---|
| | 421 | #Unsupported |
|---|
| | 422 | nil |
|---|
| | 423 | end |
|---|
| | 424 | end |
|---|
| | 425 | end |
|---|
| | 426 | |
|---|
| | 427 | options[:include] = ([:translations] << options[:include]).compact.flatten |
|---|
| | 428 | args << options |
|---|
| | 429 | untranslated_find(*args) |
|---|
| | 430 | end |
|---|
| | 431 | end |
|---|
| | 432 | end |
|---|
| | 433 | |
|---|
| | 434 | def add_bidi(value, facet) |
|---|
| | 435 | return value unless Locale.active? |
|---|
| | 436 | value.direction = self.send("\#{facet}_is_base?") ? |
|---|
| | 437 | (Locale.base_language ? Locale.base_language.direction : nil) : |
|---|
| | 438 | (Locale.active ? Locale.language.direction : nil) |
|---|
| | 439 | |
|---|
| | 440 | # insert bidi embedding characters, if necessary |
|---|
| | 441 | if @@facet_options[facet][:bidi] && |
|---|
| | 442 | Locale.language && Locale.language.direction && value.direction |
|---|
| | 443 | if Locale.language.direction == 'ltr' && value.direction == 'rtl' |
|---|
| | 444 | bidi_str = "\xe2\x80\xab" + value + "\xe2\x80\xac" |
|---|
| | 445 | bidi_str.direction = value.direction |
|---|
| | 446 | return bidi_str |
|---|
| | 447 | elsif Locale.language.direction == 'rtl' && value.direction == 'ltr' |
|---|
| | 448 | bidi_str = "\xe2\x80\xaa" + value + "\xe2\x80\xac" |
|---|
| | 449 | bidi_str.direction = value.direction |
|---|
| | 450 | return bidi_str |
|---|
| | 451 | end |
|---|
| | 452 | end |
|---|
| | 453 | return value |
|---|
| | 454 | end |
|---|
| | 455 | |
|---|
| | 456 | extend Globalize::DbTranslate::ViaAssociationStorageClassMethods |
|---|
| | 457 | |
|---|
| | 458 | HERE |
|---|
| | 459 | |
|---|
| | 460 | facets.each do |facet| |
|---|
| | 461 | bidi = (!(options[facet] && !options[facet][:bidi_embed])).to_s |
|---|
| | 462 | fallback = (options[facet] && options[facet].key?(:fallback)) ? |
|---|
| | 463 | options[facet][:fallback] : |
|---|
| | 464 | options[:fallback] |
|---|
| | 465 | base_as_default = (options[facet] && options[facet].key?(:base_as_default)) ? |
|---|
| | 466 | options[facet][:base_as_default] : |
|---|
| | 467 | options[:base_as_default] |
|---|
| | 468 | |
|---|
| | 469 | class_eval <<-HERE |
|---|
| | 470 | @@facet_options[:#{facet}] ||= {} |
|---|
| | 471 | @@facet_options[:#{facet}][:bidi] = #{bidi} |
|---|
| | 472 | @@facet_options[:#{facet}][:fallback] = #{fallback} |
|---|
| | 473 | @@facet_options[:#{facet}][:base_as_default] = #{base_as_default} |
|---|
| | 474 | |
|---|
| | 475 | @@translation_reflections << :#{facet}_translations |
|---|
| | 476 | @@translation_dyn_reflections << :#{facet}_dyn_translations |
|---|
| | 477 | |
|---|
| | 478 | case @@dyn_table_aliases.size |
|---|
| | 479 | when 0 |
|---|
| | 480 | @@dyn_table_aliases[:#{facet}] = "globalize_translations" |
|---|
| | 481 | else |
|---|
| | 482 | @@dyn_table_aliases[:#{facet}] = "#{facet}_dyn_translations_" + self.table_name |
|---|
| | 483 | end |
|---|
| | 484 | |
|---|
| | 485 | case @@aliases.size |
|---|
| | 486 | when 0 |
|---|
| | 487 | @@aliases[:#{facet}] = "globalize_translations.text" |
|---|
| | 488 | when 1 |
|---|
| | 489 | @@aliases[:#{facet}] = self.to_s.tableize + "_globalize_translations.text" |
|---|
| | 490 | else |
|---|
| | 491 | @@aliases[:#{facet}] = self.to_s.tableize + "_globalize_translations" + "2" + ".text" # this is a guess |
|---|
| | 492 | end |
|---|
| | 493 | |
|---|
| | 494 | has_many :#{facet}_translations, :class_name => '::Globalize::ModelTranslation', |
|---|
| | 495 | :conditions => ['globalize_translations.table_name = ? AND globalize_translations.facet = ?', table_name, "#{facet}"], |
|---|
| | 496 | :foreign_key => 'item_id', |
|---|
| | 497 | :dependent => :delete_all |
|---|
| | 498 | |
|---|
| | 499 | #Used exclusively for globalizing dynamic finders (see method_missing) |
|---|
| | 500 | has_many :#{facet}_dyn_translations, :class_name => '::Globalize::ModelTranslation', |
|---|
| | 501 | :conditions => [@@dyn_table_aliases[:#{facet}] + '.table_name = ? AND ' + @@dyn_table_aliases[:#{facet}] + '.facet = ?', table_name, "#{facet}"], |
|---|
| | 502 | :foreign_key => 'item_id', |
|---|
| | 503 | :dependent => :delete_all |
|---|
| | 504 | |
|---|
| | 505 | |
|---|
| | 506 | def #{facet} |
|---|
| | 507 | unless Locale.base? |
|---|
| | 508 | translation = self.#{facet}_translations.detect{|tr| tr.language_id == Locale.language.id } |
|---|
| | 509 | |
|---|
| | 510 | unless translation |
|---|
| | 511 | if @@facet_options[:#{facet}][:fallback] |
|---|
| | 512 | Locale.active.possible_languages.each do |fallback| |
|---|
| | 513 | unless Locale.base_language.code == fallback.code |
|---|
| | 514 | translation = self.#{facet}_translations.detect{|tr| tr.language_id == fallback.id } |
|---|
| | 515 | break if translation |
|---|
| | 516 | else |
|---|
| | 517 | translation = read_attribute(:#{facet}) |
|---|
| | 518 | break if translation |
|---|
| | 519 | end |
|---|
| | 520 | end |
|---|
| | 521 | end |
|---|
| | 522 | end |
|---|
| | 523 | |
|---|
| | 524 | unless translation |
|---|
| | 525 | if @@facet_options[:#{facet}][:base_as_default] |
|---|
| | 526 | translation = read_attribute(:#{facet}) |
|---|
| | 527 | end |
|---|
| | 528 | end |
|---|
| | 529 | result = translation.nil? ? nil : (translation.kind_of?(::Globalize::ModelTranslation) ? translation.text : translation) |
|---|
| | 530 | else |
|---|
| | 531 | result = read_attribute(:#{facet}) |
|---|
| | 532 | #If the base locale value is nil and fallback is active... |
|---|
| | 533 | unless result |
|---|
| | 534 | if @@facet_options[:#{facet}][:fallback] |
|---|
| | 535 | Locale.active.possible_languages.each do |fallback| |
|---|
| | 536 | unless Locale.base_language.code == fallback.code |
|---|
| | 537 | translation = self.#{facet}_translations.detect{|tr| tr.language_id == fallback.id } |
|---|
| | 538 | break if translation |
|---|
| | 539 | end |
|---|
| | 540 | end |
|---|
| | 541 | result = translation.nil? ? nil : (translation.kind_of?(::Globalize::ModelTranslation) ? translation.text : translation) |
|---|
| | 542 | end |
|---|
| | 543 | end |
|---|
| | 544 | end |
|---|
| | 545 | |
|---|
| | 546 | result.nil? ? nil : add_bidi(result, :#{facet}) |
|---|
| | 547 | end |
|---|
| | 548 | alias :#{facet}_before_type_cast :#{facet} |
|---|
| | 549 | |
|---|
| | 550 | def #{facet}=(value) |
|---|
| | 551 | raise 'Globalize::Locale not active.' unless Locale.active? |
|---|
| | 552 | unless Locale.base? |
|---|
| | 553 | language = Locale.language |
|---|
| | 554 | translation = self.#{facet}_translations.detect{|tr| tr.language_id == language.id } |
|---|
| | 555 | if value |
|---|
| | 556 | if translation.nil? |
|---|
| | 557 | translation = self.#{facet}_translations.build(:table_name => self.class.table_name, |
|---|
| | 558 | :item_id => self.id, :facet => "#{facet}", :language_id => language.id) |
|---|
| | 559 | end |
|---|
| | 560 | translation.text = value |
|---|
| | 561 | translation.save! unless translation.new_record? |
|---|
| | 562 | else |
|---|
| | 563 | translation.destroy && self.#{facet}_translations(true) if translation |
|---|
| | 564 | end |
|---|
| | 565 | else |
|---|
| | 566 | write_attribute(:#{facet}, value) |
|---|
| | 567 | end |
|---|
| | 568 | end |
|---|
| | 569 | |
|---|
| | 570 | def #{facet}_is_base? |
|---|
| | 571 | unless Locale.base? |
|---|
| | 572 | translation = self.#{facet}_translations.detect{|tr| tr.language_id == Locale.language.id } |
|---|
| | 573 | result = translation.nil? |
|---|
| | 574 | else |
|---|
| | 575 | true |
|---|
| | 576 | end |
|---|
| | 577 | end |
|---|
| | 578 | |
|---|
| | 579 | HERE |
|---|
| | 580 | end |
|---|
| | 581 | end |
|---|
| | 1310 | |
|---|
| | 1311 | module ViaAssociationStorageClassMethods |
|---|
| | 1312 | |
|---|
| | 1313 | private |
|---|
| | 1314 | |
|---|
| | 1315 | # Overridden to ensure that dynamic finders using localized attributes |
|---|
| | 1316 | # like find_by_user_name(user_name) or find_by_user_name_and_password(user_name, password) |
|---|
| | 1317 | # use the appropriately localized column. |
|---|
| | 1318 | # |
|---|
| | 1319 | # Note: <i>Used when Globalize::DbTranslate.storage_method = :same_table</i> |
|---|
| | 1320 | def method_missing(method_id, *arguments) |
|---|
| | 1321 | if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(method_id.to_s) |
|---|
| | 1322 | finder, deprecated_finder = determine_finder(match), determine_deprecated_finder(match) |
|---|
| | 1323 | |
|---|
| | 1324 | facets = extract_attribute_names_from_match(match) |
|---|
| | 1325 | super unless all_attributes_exists?(facets) |
|---|
| | 1326 | |
|---|
| | 1327 | attributes = construct_attributes_from_arguments(facets, arguments) |
|---|
| | 1328 | |
|---|
| | 1329 | unless Locale.base? |
|---|
| | 1330 | conditions = [] |
|---|
| | 1331 | keys = attributes.keys.collect do |k| |
|---|
| | 1332 | replaced = self.send(:class_variable_get, :@@dyn_table_aliases)[k.intern] |
|---|
| | 1333 | replaced ? "#{replaced}.text" : k |
|---|
| | 1334 | end |
|---|
| | 1335 | conditions << keys.collect {|k| "#{k} = ?"}.join(' AND ') |
|---|
| | 1336 | conditions += attributes.values |
|---|
| | 1337 | |
|---|
| | 1338 | attributes = conditions |
|---|
| | 1339 | end |
|---|
| | 1340 | |
|---|
| | 1341 | extra_options = arguments[facets.size] |
|---|
| | 1342 | extra_options ||= {} |
|---|
| | 1343 | includes = self.send(:class_variable_get, :@@translation_dyn_reflections).dup |
|---|
| | 1344 | extra_options[:include] = (includes << extra_options[:include]).compact.flatten |
|---|
| | 1345 | |
|---|
| | 1346 | case extra_options |
|---|
| | 1347 | when nil |
|---|
| | 1348 | options = { :conditions => attributes } |
|---|
| | 1349 | set_readonly_option!(options) |
|---|
| | 1350 | ActiveSupport::Deprecation.silence { send(finder, options) } |
|---|
| | 1351 | |
|---|
| | 1352 | when Hash |
|---|
| | 1353 | finder_options = extra_options.merge(:conditions => attributes) |
|---|
| | 1354 | validate_find_options(finder_options) |
|---|
| | 1355 | set_readonly_option!(finder_options) |
|---|
| | 1356 | |
|---|
| | 1357 | if extra_options[:conditions] |
|---|
| | 1358 | with_scope(:find => { :conditions => extra_options[:conditions] }) do |
|---|
| | 1359 | ActiveSupport::Deprecation.silence { send(finder, finder_options) } |
|---|
| | 1360 | end |
|---|
| | 1361 | else |
|---|
| | 1362 | ActiveSupport::Deprecation.silence { send(finder, finder_options) } |
|---|
| | 1363 | end |
|---|
| | 1364 | |
|---|
| | 1365 | else |
|---|
| | 1366 | ActiveSupport::Deprecation.silence do |
|---|
| | 1367 | send(deprecated_finder, sanitize_sql(attributes), *arguments[facets.length..-1]) |
|---|
| | 1368 | end |
|---|
| | 1369 | end |
|---|
| | 1370 | elsif match = /find_or_(initialize|create)_by_([_a-zA-Z]\w*)/.match(method_id.to_s) |
|---|
| | 1371 | instantiator = determine_instantiator(match) |
|---|
| | 1372 | facets = extract_attribute_names_from_match(match) |
|---|
| | 1373 | super unless all_attributes_exists?(facets) |
|---|
| | 1374 | |
|---|
| | 1375 | attributes = construct_attributes_from_arguments(facets, arguments) |
|---|
| | 1376 | |
|---|
| | 1377 | unless Locale.base? |
|---|
| | 1378 | conditions = [] |
|---|
| | 1379 | keys = attributes.keys.collect do |k| |
|---|
| | 1380 | replaced = self.send(:class_variable_get, :@@dyn_table_aliases)[k.intern] |
|---|
| | 1381 | replaced ? "#{replaced}.text" : k |
|---|
| | 1382 | end |
|---|
| | 1383 | conditions << keys.collect {|k| "#{k} = ?"}.join(' AND ') |
|---|
| | 1384 | conditions += attributes.values |
|---|
| | 1385 | |
|---|
| | 1386 | attributes = conditions |
|---|
| | 1387 | end |
|---|
| | 1388 | |
|---|
| | 1389 | includes = self.send(:class_variable_get, :@@translation_dyn_reflections).dup |
|---|
| | 1390 | options = { :conditions => attributes, :include => includes} |
|---|
| | 1391 | set_readonly_option!(options) |
|---|
| | 1392 | |
|---|
| | 1393 | find_initial(options) || send(instantiator, attributes) |
|---|
| | 1394 | else |
|---|
| | 1395 | super |
|---|
| | 1396 | end |
|---|
| | 1397 | end |
|---|
| | 1398 | end #ViaAssociationStorageClassMethods |
|---|
| | 1399 | |
|---|