📄 db_translate.rb
字号:
return if Locale.base?
table_name = self.class.table_name
self.class.globalize_facets.each do |facet|
next unless has_attribute?(facet)
text = read_attribute(facet)
language_id = Locale.active.language.id
tr = ModelTranslation.find(:first, :conditions =>
[ "table_name = ? AND item_id = ? AND facet = ? AND language_id = ?",
table_name, id, facet.to_s, language_id ])
if tr.nil?
# create new record
ModelTranslation.create(:table_name => table_name,
:item_id => id, :facet => facet.to_s,
:language_id => language_id,
:text => text) unless text.nil?
elsif text.blank?
# delete record
tr.destroy
else
# update record
tr.update_attribute(:text, text) if tr.text != text
end
end # end facets loop
end
end
module TranslateClassMethods
=begin rdoc
This is a replacement for the standard ActiveRecord +find+ method.
Use it in the exact same way you would the regular find, except for the
following provisos:
1. At this point, it will not work with the <tt>:include</tt> option...
1. However, there is a replacement: <tt>:include_translated</tt>, which will
be described below.
1. The <tt>:select</tt> option is prohibited.
+find+ returns the retreived models, with all translated fields correctly
loaded, depending on the active language.
<tt>:include_translated</tt> works as follows:
any model specified in the <tt>:include_translated</tt> option
will be eagerly loaded and added to the current model as attributes,
prefixed with the name of the associated model. This is often referred
to as _piggybacking_.
Example:
class Product < ActiveRecord::Base
belongs_to :manufacturer
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :products
translates :name
end
prods = Product.find(:all, :include_translated => [ :manufacturer, :category ])
prods.first.category_name -> "batedeira"
=end
def find(*args)
options = args.last.is_a?(Hash) ? args.last : {}
return globalize_old_find(*args) if options[:untranslated]
find_type = args.first
if find_type == :first
options[:translate_all] = true
return globalize_old_find(:first, options)
elsif find_type != :all
return globalize_old_find(*args)
end
raise StandardError,
":select option not allowed on translatable models " +
"(#{options[:select]})" if options[:select] && !options[:select].empty?
# do quick version if base language is active
if Locale.base? && !options.has_key?(:include_translated)
results = untranslated_find(*args)
results.each {|result|
result.set_original_language
}
return results
end
options[:conditions] = fix_conditions(options[:conditions]) if options[:conditions]
# there will at least be an +id+ field here
select_clause = untranslated_fields.map {|f| "#{table_name}.#{f}" }.join(", ")
joins_clause = options[:joins].nil? ? "" : options[:joins].dup
joins_args = []
load_full = options[:translate_all]
facets = load_full ? globalize_facets : preload_facets
if Locale.base?
select_clause << ', ' << facets.map {|f| "#{table_name}.#{f}" }.join(", ")
else
language_id = Locale.active.language.id
load_full = options[:translate_all]
facets = load_full ? globalize_facets : preload_facets
=begin
There's a bug in sqlite that messes up sorting when aliasing fields,
see: <http://www.sqlite.org/cvstrac/tktview?tn=1521,33>.
Since I want to use sqlite, and sorting, I'm hacking this to make it work.
This involves renaming order by fields and adding them to the SELECT part.
It's a sucky hack, but hopefully sqlite will fix the bug soon.
=end
# sqlite bug hack
select_position = untranslated_fields.size
# initialize where tweaking
if options[:conditions].nil?
where_clause = ""
else
if options[:conditions].kind_of? Array
conditions_is_array = true
where_clause = options[:conditions].shift
else
where_clause = options[:conditions]
end
end
facets.each do |facet|
facet = facet.to_s
facet_table_alias = "t_#{facet}"
# sqlite bug hack
select_position += 1
options[:order].sub!(/\b#{facet}\b/, select_position.to_s) if options[:order] && sqlite?
select_clause << ", COALESCE(#{facet_table_alias}.text, #{table_name}.#{facet}) AS #{facet}, "
select_clause << " #{facet_table_alias}.text AS #{facet}_not_base "
joins_clause << " LEFT OUTER JOIN globalize_translations AS #{facet_table_alias} " +
"ON #{facet_table_alias}.table_name = ? " +
"AND #{table_name}.#{primary_key} = #{facet_table_alias}.item_id " +
"AND #{facet_table_alias}.facet = ? AND #{facet_table_alias}.language_id = ? "
joins_args << table_name << facet << language_id
#for translated fields inside WHERE clause substitute corresponding COALESCE string
where_clause.gsub!(/((((#{table_name}\.)|\W)#{facet})|^#{facet})\W/, " COALESCE(#{facet_table_alias}.text, #{table_name}.#{facet}) ")
end
options[:conditions] = sanitize_sql(
conditions_is_array ? [ where_clause ] + options[:conditions] : where_clause
) unless options[:conditions].nil?
end
# add in associations (of :belongs_to nature) if applicable
associations = options[:include_translated] || []
associations = [ associations ].flatten
associations.each do |assoc|
rfxn = reflect_on_association(assoc)
assoc_type = rfxn.macro
raise StandardError,
":include_translated associations must be of type :belongs_to;" +
"#{assoc} is #{assoc_type}" if assoc_type != :belongs_to
klass = rfxn.klass
assoc_facets = klass.preload_facets
included_table = klass.table_name
included_fk = klass.primary_key
fk = rfxn.options[:foreign_key] || "#{assoc}_id"
assoc_facets.each do |facet|
facet_table_alias = "t_#{assoc}_#{facet}"
if Locale.base?
select_clause << ", #{included_table}.#{facet} AS #{assoc}_#{facet} "
else
select_clause << ", COALESCE(#{facet_table_alias}.text, #{included_table}.#{facet}) " +
"AS #{assoc}_#{facet} "
joins_clause << " LEFT OUTER JOIN globalize_translations AS #{facet_table_alias} " +
"ON #{facet_table_alias}.table_name = ? " +
"AND #{table_name}.#{fk} = #{facet_table_alias}.item_id " +
"AND #{facet_table_alias}.facet = ? AND #{facet_table_alias}.language_id = ? "
joins_args << klass.table_name << facet.to_s << language_id
end
end
joins_clause << "LEFT OUTER JOIN #{included_table} " +
"ON #{table_name}.#{fk} = #{included_table}.#{included_fk} "
end
options[:select] = select_clause
options[:readonly] = false
sanitized_joins_clause = sanitize_sql( [ joins_clause, *joins_args ] )
options[:joins] = sanitized_joins_clause
results = globalize_old_find(:all, options)
results.each {|result|
result.set_original_language
result.fully_loaded = true if load_full
}
end
# Use this instead of +find+ if you want to bypass the translation
# code for any reason.
def untranslated_find(*args)
has_options = args.last.is_a?(Hash)
options = has_options ? args.last : {}
options[:untranslated] = true
args << options if !has_options
globalize_old_find(*args)
end
protected
def validate_find_options(options) # :nodoc:
options.assert_valid_keys [ :conditions, :group, :include, :include_translated,
:group, :joins, :limit, :offset, :order, :select, :readonly, :translate_all,
:untranslated ]
end
private
# properly scope conditions to table
def fix_conditions(conditions)
if conditions.kind_of? Array
is_array = true
sql = conditions.shift
else
is_array = false
sql = conditions
end
column_names.each do |column_name|
sql.gsub!( /(^|([^\.\w"'`]+))(["'`]?)#{column_name}(?!\w)/,
'\1' + "#{table_name}." + '\3' + "#{column_name}" )
end
if is_array
[ sql ] + conditions
else
sql
end
end
end
end
end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -