globalize 3.1.0 → 4.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,7 +19,6 @@ module Globalize
19
19
  end
20
20
 
21
21
  def write(locale, name, value)
22
- #raise 'z' if value.nil? # TODO
23
22
  self[locale][name.to_s] = value
24
23
  end
25
24
  end
@@ -4,16 +4,16 @@ module Globalize
4
4
  delegate :translated_locales, :set_translations_table_name, :to => :translation_class
5
5
 
6
6
  def with_locales(*locales)
7
- scoped.merge(translation_class.with_locales(*locales))
7
+ all.merge translation_class.with_locales(*locales)
8
8
  end
9
9
 
10
10
  def with_translations(*locales)
11
11
  locales = translated_locales if locales.empty?
12
- includes(:translations).with_locales(locales).with_required_attributes
12
+ includes(:translations).with_locales(locales).with_required_attributes.references(:translations)
13
13
  end
14
14
 
15
15
  def with_required_attributes
16
- required_translated_attributes.inject(scoped) do |scope, name|
16
+ required_translated_attributes.inject(all) do |scope, name|
17
17
  scope.where("#{translated_column_name(name)} IS NOT NULL")
18
18
  end
19
19
  end
@@ -58,18 +58,22 @@ module Globalize
58
58
  "#{translation_class.table_name}.#{name}"
59
59
  end
60
60
 
61
- if RUBY_VERSION < '1.9'
62
- def respond_to?(method_id, *args, &block)
63
- supported_on_missing?(method_id) || super
64
- end
65
- else
66
- def respond_to_missing?(method_id, include_private = false)
67
- supported_on_missing?(method_id) || super
61
+ def relation
62
+ relation = Relation.new(self, arel_table)
63
+
64
+ if finder_needs_type_condition?
65
+ relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
66
+ else
67
+ relation
68
68
  end
69
69
  end
70
70
 
71
+ def respond_to_missing?(method_id, include_private = false)
72
+ supported_on_missing?(method_id) || super
73
+ end
74
+
71
75
  def supported_on_missing?(method_id)
72
- return super unless RUBY_VERSION < '1.9' || respond_to?(:translated_attribute_names)
76
+ return super unless respond_to?(:translated_attribute_names)
73
77
  match = defined?(::ActiveRecord::DynamicFinderMatch) && (::ActiveRecord::DynamicFinderMatch.match(method_id) || ::ActiveRecord::DynamicScopeMatch.match(method_id))
74
78
  return false if match.nil?
75
79
 
@@ -86,7 +90,7 @@ module Globalize
86
90
  match, attribute_names, translated_attributes, untranslated_attributes = supported_on_missing?(method_id)
87
91
  return super unless match
88
92
 
89
- scope = scoped
93
+ scope = all
90
94
 
91
95
  translated_attributes.each do |attr|
92
96
  scope = scope.with_translated_attribute(attr, arguments[attribute_names.index(attr)])
@@ -99,20 +103,20 @@ module Globalize
99
103
  end
100
104
 
101
105
  if defined?(::ActiveRecord::DynamicFinderMatch) && match.is_a?(::ActiveRecord::DynamicFinderMatch)
102
- if match.instantiator? and scope.blank?
106
+ if match.instantiator? && scope.blank?
103
107
  return scope.find_or_instantiator_by_attributes match, attribute_names, *arguments, &block
104
108
  end
105
109
  match_finder_method = match.finder.to_s
106
- match_finder_method << "!" if match.bang? && ::ActiveRecord::VERSION::STRING >= "3.1.0"
110
+ match_finder_method << "!" if match.bang?
107
111
  return scope.send(match_finder_method).tap do |found|
108
- found.is_a?(Array) ? found.map { |f| f.translations.reload } : found.translations.reload unless found.nil?
112
+ Array(found).map { |f| f.translations.reload } unless found.nil?
109
113
  end
110
114
  end
111
115
  return scope
112
116
  end
113
117
 
114
118
  def find_or_instantiator_by_attributes(match, attributes, *args)
115
- options = args.size > 1 && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
119
+ options = args.many? && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
116
120
  protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
117
121
  args.each_with_index do |arg, i|
118
122
  if arg.is_a?(Hash)
@@ -122,15 +126,8 @@ module Globalize
122
126
  end
123
127
  end
124
128
 
125
- record = if ::ActiveRecord::VERSION::STRING < "3.1.0"
126
- new do |r|
127
- r.send(:attributes=, protected_attributes_for_create, true) unless protected_attributes_for_create.empty?
128
- r.send(:attributes=, unprotected_attributes_for_create, false) unless unprotected_attributes_for_create.empty?
129
- end
130
- else
131
- new(protected_attributes_for_create, options) do |r|
132
- r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
133
- end
129
+ record = new(protected_attributes_for_create, options) do |r|
130
+ r.assign_attributes(unprotected_attributes_for_create)
134
131
  end
135
132
  yield(record) if block_given?
136
133
  record.send(match.bang? ? :save! : :save) if match.instantiator.eql?(:create)
@@ -138,33 +135,7 @@ module Globalize
138
135
  record
139
136
  end
140
137
 
141
- private
142
-
143
- # Override the default relation method in order to return a subclass
144
- # of ActiveRecord::Relation with custom where_values_hash method,
145
- # for use in find_or_create_by_instantiator (Rails version >= 3.2.1)
146
- if ::ActiveRecord::VERSION::STRING >= "3.2.1"
147
- def relation
148
- relation = globalize_relation_class.new(self, arel_table)
149
-
150
- if finder_needs_type_condition?
151
- relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
152
- else
153
- relation
154
- end
155
- end
156
-
157
- # Use pattern defined in FriendlyId 4.x to avoid conflict with any other
158
- # gems (such as FriendlyId) which override relation method.
159
- def globalize_relation_class
160
- @globalize_relation_class ||= Class.new(relation_without_globalize.class).tap do |klass|
161
- klass.send :include, QueryMethods
162
- const_set('GlobalizeActiveRecordRelation', klass)
163
- end
164
- end
165
- end
166
-
167
- protected
138
+ protected
168
139
 
169
140
  def translated_attr_accessor(name)
170
141
  define_method(:"#{name}=") do |value|
@@ -11,69 +11,43 @@ module Globalize
11
11
  super.merge(translated_attributes)
12
12
  end
13
13
 
14
- def self.included(base)
15
- # Maintain Rails 3.0.x compatibility while adding Rails 3.1.x compatibility
16
- if base.method_defined?(:assign_attributes)
17
- base.class_eval %{
18
- def assign_attributes(attributes, options = {})
19
- with_given_locale(attributes) { super }
20
- end
21
- }
22
- else
23
- base.class_eval %{
24
- def attributes=(attributes, *args)
25
- with_given_locale(attributes) { super }
26
- end
27
-
28
- def update_attributes!(attributes, *args)
29
- with_given_locale(attributes) { super }
30
- end
31
-
32
- def update_attributes(attributes, *args)
33
- with_given_locale(attributes) { super }
34
- end
35
- }
36
- end
14
+ def attributes=(attributes, *args)
15
+ with_given_locale(attributes) { super }
16
+ end
17
+
18
+ def assign_attributes(attributes, *args)
19
+ with_given_locale(attributes) { super }
37
20
  end
38
21
 
39
22
  def write_attribute(name, value, options = {})
40
- if translated?(name)
41
- # Deprecate old use of locale
42
- unless options.is_a?(Hash)
43
- warn "[DEPRECATION] passing 'locale' as #{options.inspect} is deprecated. Please use {:locale => #{options.inspect}} instead."
44
- options = {:locale => options}
45
- end
46
- options = {:locale => Globalize.locale}.merge(options)
47
-
48
- # Dirty tracking, paraphrased from
49
- # ActiveRecord::AttributeMethods::Dirty#write_attribute.
50
- name_str = name.to_s
51
- if attribute_changed?(name_str)
52
- # If there's already a change, delete it if this undoes the change.
53
- old = changed_attributes[name_str]
54
- changed_attributes.delete(name_str) if value == old
55
- else
56
- # If there's not a change yet, record it.
57
- old = globalize.fetch(options[:locale], name)
58
- old = old.clone if old.duplicable?
59
- changed_attributes[name_str] = old if value != old
60
- end
61
-
62
- globalize.write(options[:locale], name, value)
23
+ return super(name, value) unless translated?(name)
24
+
25
+ options = {:locale => Globalize.locale}.merge(options)
26
+
27
+ # Dirty tracking, paraphrased from
28
+ # ActiveRecord::AttributeMethods::Dirty#write_attribute.
29
+ name_str = name.to_s
30
+ if attribute_changed?(name_str)
31
+ # If there's already a change, delete it if this undoes the change.
32
+ old = changed_attributes[name_str]
33
+ changed_attributes.delete(name_str) if value == old
63
34
  else
64
- super(name, value)
35
+ # If there's not a change yet, record it.
36
+ old = globalize.fetch(options[:locale], name)
37
+ old = old.dup if old.duplicable?
38
+ changed_attributes[name_str] = old if value != old
65
39
  end
40
+
41
+ globalize.write(options[:locale], name, value)
66
42
  end
67
43
 
68
44
  def read_attribute(name, options = {})
69
- # Deprecate old use of locale
70
- unless options.is_a?(Hash)
71
- warn "[DEPRECATION] passing 'locale' as #{options.inspect} is deprecated. Please use {:locale => #{options.inspect}} instead."
72
- options = {:locale => options}
73
- end
74
-
75
45
  options = {:translated => true, :locale => nil}.merge(options)
76
- if self.class.translated?(name) and options[:translated]
46
+ return super(name) unless options[:translated]
47
+
48
+ if name == :locale
49
+ self.try(:locale).presence || self.translation.locale
50
+ elsif self.class.translated?(name)
77
51
  if (value = globalize.fetch(options[:locale] || Globalize.locale, name))
78
52
  value
79
53
  else
@@ -88,24 +62,24 @@ module Globalize
88
62
  translated_attribute_names.map(&:to_s) + super
89
63
  end
90
64
 
91
- def translated?(name)
92
- self.class.translated?(name)
93
- end
65
+ delegate :translated?, :to => :class
94
66
 
95
67
  def translated_attributes
96
68
  translated_attribute_names.inject({}) do |attributes, name|
97
- attributes.merge(name.to_s => translation.send(name))
69
+ if self.respond_to?(name) && Globalize.locale == I18n.default_locale
70
+ attributes.merge(name.to_s => self.send(name))
71
+ else
72
+ attributes.merge(name.to_s => translation.send(name))
73
+ end
98
74
  end
99
75
  end
100
76
 
101
77
  # This method is basically the method built into Rails
102
78
  # but we have to pass {:translated => false}
103
79
  def untranslated_attributes
104
- attrs = {}
105
- attribute_names.each do |name|
106
- attrs[name] = read_attribute(name, {:translated => false})
80
+ attribute_names.inject({}) do |attrs, name|
81
+ attrs[name] = read_attribute(name, {:translated => false}); attrs
107
82
  end
108
- attrs
109
83
  end
110
84
 
111
85
  def set_translations(options)
@@ -168,22 +142,16 @@ module Globalize
168
142
  translation_caches[::Globalize.locale] = translation.previous_version
169
143
  end
170
144
 
171
- def column_for_attribute name
172
- translated_attribute_names.include?(name) ? globalize.send(:column_for_attribute, name) : super
173
- end
174
-
175
- private
176
-
177
- def update(*)
178
- I18n.with_locale(read_attribute(:locale) || I18n.default_locale) do
145
+ def save(*)
146
+ Globalize.with_locale(read_attribute(:locale) || I18n.default_locale) do
179
147
  super
180
148
  end
181
149
  end
182
150
 
183
- def create(*)
184
- I18n.with_locale(read_attribute(:locale) || I18n.default_locale) do
185
- super
186
- end
151
+ def column_for_attribute name
152
+ return super if translated_attribute_names.exclude?(name)
153
+
154
+ globalize.send(:column_for_attribute, name)
187
155
  end
188
156
 
189
157
  protected
@@ -68,7 +68,7 @@ module Globalize
68
68
 
69
69
  def create_translation_table
70
70
  connection.create_table(translations_table_name) do |t|
71
- t.references table_name.sub(/^#{table_name_prefix}/, '').singularize
71
+ t.references table_name.sub(/^#{table_name_prefix}/, '').singularize, :null => false
72
72
  t.string :locale, :null => false
73
73
  t.timestamps
74
74
  end
@@ -130,7 +130,7 @@ module Globalize
130
130
  end
131
131
 
132
132
  # Now, update the actual model's record with the hash.
133
- @model.update_all(fields_to_update, {:id => translated_record['id']})
133
+ @model.where(:id => translated_record['id']).update_all(fields_to_update)
134
134
  end
135
135
  end
136
136
 
@@ -0,0 +1,63 @@
1
+ module Globalize
2
+ module ActiveRecord
3
+ class Relation < ::ActiveRecord::Relation
4
+
5
+ attr_accessor :translations_reload_needed
6
+
7
+ class WhereChain < ::ActiveRecord::QueryMethods::WhereChain
8
+ def not(*args)
9
+ if @scope.parse_translated_conditions!(*args)
10
+ @scope.translations_reload_needed = true
11
+ @scope.with_translations_in_this_locale.where.not(*args)
12
+ else
13
+ super
14
+ end
15
+ end
16
+ end
17
+
18
+ def where(opts = :chain, *rest)
19
+ if opts == :chain
20
+ WhereChain.new(spawn)
21
+ elsif parse_translated_conditions!(opts, *rest)
22
+ self.translations_reload_needed = true
23
+ super.with_translations_in_this_locale
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ def exists?(conditions = :none)
30
+ if parse_translated_conditions!(conditions)
31
+ with_translations_in_this_locale.exists?(conditions)
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ %w[ first last take ].each do |method_name|
38
+ eval <<-END_RUBY
39
+ def #{method_name}
40
+ super.tap do |f|
41
+ if translations_reload_needed
42
+ f.translations.reload
43
+ translations_reload_needed = false
44
+ end
45
+ end
46
+ end
47
+ END_RUBY
48
+ end
49
+
50
+ def with_translations_in_this_locale
51
+ with_translations(Globalize.locale)
52
+ end
53
+
54
+ def parse_translated_conditions!(opts, *rest)
55
+ if opts.is_a?(Hash) && (keys = opts.symbolize_keys.keys & translated_attribute_names).present?
56
+ keys.each do |key|
57
+ opts[translated_column_name(key)] = opts.delete(key) || opts.delete(key.to_s)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -2,7 +2,6 @@ module Globalize
2
2
  module ActiveRecord
3
3
  class Translation < ::ActiveRecord::Base
4
4
 
5
- attr_accessible :locale
6
5
  validates :locale, :presence => true
7
6
 
8
7
  class << self
@@ -0,0 +1,5 @@
1
+ module Globalize
2
+ module Versioning
3
+ autoload :PaperTrail, 'globalize/versioning/paper_trail'
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ require 'paper_trail'
2
+
3
+ module Globalize
4
+ module Versioning
5
+ module PaperTrail
6
+ # At present this isn't used but we may use something similar in paper trail
7
+ # shortly, so leaving it around to reference easily.
8
+ #def versioned_columns
9
+ #super + self.class.translated_attribute_names
10
+ #end
11
+ end
12
+ end
13
+ end
14
+
15
+ ActiveRecord::Base.class_eval do
16
+ class << self
17
+ def has_paper_trail_with_globalize(*args)
18
+ has_paper_trail_without_globalize(*args)
19
+ include Globalize::Versioning::PaperTrail
20
+ end
21
+ alias_method_chain :has_paper_trail, :globalize
22
+ end
23
+ end
24
+
25
+ PaperTrail::Version.class_eval do
26
+
27
+ before_save do |version|
28
+ version.locale = Globalize.locale.to_s
29
+ end
30
+
31
+ def self.for_this_locale
32
+ where :locale => Globalize.locale.to_s
33
+ end
34
+
35
+ def sibling_versions_with_locales
36
+ sibling_versions_without_locales.for_this_locale
37
+ end
38
+ alias_method_chain :sibling_versions, :locales
39
+ end
@@ -0,0 +1,20 @@
1
+ module ActiveRecord
2
+ class Relation
3
+ if ::ActiveRecord::VERSION::STRING >= "3.2.1"
4
+ def where_values_hash
5
+ _translations_table_name = klass.respond_to?(:translations_table_name) ? klass.translations_table_name : nil
6
+
7
+ equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
8
+ [table_name, _translations_table_name].compact.include? node.left.relation.name
9
+ }
10
+
11
+ binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
12
+
13
+ Hash[equalities.map { |where|
14
+ name = where.left.name
15
+ [name, binds.fetch(name.to_s) { where.right } ]
16
+ }].with_indifferent_access
17
+ end
18
+ end
19
+ end
20
+ end