easy_translatable 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2be2c2dc9fc20ce961fad405019f1579d98d1958
4
- data.tar.gz: decf8b50cd1b08f0f8da58f3587d042b7c87126f
3
+ metadata.gz: b058f1009620ef66e0eb9a2425af27841e3f4d61
4
+ data.tar.gz: 97066aa3b7af92612aff25f95896be71a0f70174
5
5
  SHA512:
6
- metadata.gz: 32082ff14221d9002f9ab63bd8cec4cd7660f31d447a8e8dda5d29b4c2fd217eccbcd82adb5539555a1c73d5fe5b20b7f5f0d4fa166213d6747e753d6ffdbc6b
7
- data.tar.gz: 5edad626aba2bcda39997ac8966871c1ea9122d6b44d86e7ae44eb47771eb9066b4b109a29e65484a164556475e754e0751c5b3ed228e591cb997d9e1bf675d1
6
+ metadata.gz: 29f96b7b1d821caafea24001ba52d15350527d3df865819a60b17cfafea5fa2aab40459c04be70935b624597ed8f90b68905c79ba2703474d72e9fc63625259e
7
+ data.tar.gz: 40ddc1c5111c3b3d1ac02bf2d75d87e8349e24d043b0b345b361a28e804e227f122aa17f30e2c833eda46090b4e74b8dca7b929df4bc6084da11ea80b135444e
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # Translatable
2
+
3
+ ## Requirements
4
+
5
+ * ActiveRecord 3.2
6
+ * I18n
7
+
8
+ ## Installation
9
+
10
+ To install just use:
11
+
12
+ ```ruby
13
+ gem install easy_translatable
14
+ ```
15
+
16
+ When using bundler put this in your Gemfile:
17
+
18
+ ```ruby
19
+ gem 'easy_translatable', '~> 0.1.0'
20
+ ```
@@ -0,0 +1,43 @@
1
+ module Translatable
2
+ module ActiveRecord
3
+ class Adapter
4
+ # The cache caches attributes that already were looked up for read access.
5
+ # The stash keeps track of new or changed values that need to be saved.
6
+ attr_accessor :record, :stash, :translations
7
+ private :record=, :stash=
8
+
9
+ def initialize(record)
10
+ self.record = record
11
+ self.stash = Attributes.new
12
+ end
13
+
14
+ def fetch_stash(locale, name)
15
+ value = stash.read(locale, name)
16
+ return value if value
17
+ return nil
18
+ end
19
+
20
+ def stash_contains?(locale, name)
21
+ stash.contains?(locale, name)
22
+ end
23
+
24
+ def fetch(locale, name)
25
+ value = stash_contains?(locale, name) ? fetch_stash(locale, name) : fetch_attribute(locale, name)
26
+
27
+ return value
28
+ end
29
+
30
+ def write(locale, name, value)
31
+ stash.write(locale, name, value)
32
+ end
33
+
34
+ protected
35
+
36
+ def fetch_attribute(locale, name)
37
+ translation = record.translation_for(locale, name, false)
38
+ return translation && translation.send(:value)
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ # Helper class for storing values per locale. Used by Globalize::Adapter
2
+ # to stash and cache attribute values.
3
+
4
+ module Translatable
5
+ module ActiveRecord
6
+ class Attributes < Hash # TODO: Think about using HashWithIndifferentAccess ?
7
+ def [](locale)
8
+ locale = locale.to_sym
9
+ self[locale] = {} unless has_key?(locale)
10
+ self.fetch(locale)
11
+ end
12
+
13
+ def contains?(locale, name)
14
+ self[locale].has_key?(name.to_s)
15
+ end
16
+
17
+ def read(locale, name)
18
+ self[locale][name.to_s]
19
+ end
20
+
21
+ def write(locale, name, value)
22
+ #raise 'z' if value.nil? # TODO
23
+ self[locale][name.to_s] = value
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ module Translatable
2
+ module ActiveRecord
3
+ module ClassMethods
4
+ def translation_class
5
+ Translatable.translation_class
6
+ end
7
+
8
+ def translated?(name)
9
+ translated_attribute_names.include?(name.to_sym)
10
+ end
11
+
12
+ protected
13
+
14
+ def translated_attr_accessor(name)
15
+ define_method(:"#{name}=") do |value|
16
+ write_attribute(name, value)
17
+ end
18
+ define_method(name) do |*args|
19
+ self.read_attribute(name, {:locale => args.detect {|a| !a.is_a? Hash }})
20
+ end
21
+ alias_method :"#{name}_before_type_cast", name
22
+ end
23
+
24
+ def locale_from(args)
25
+ args.detect {|a| !a.is_a? Hash }
26
+ end
27
+
28
+ def translations_accessor(name)
29
+ define_method(:"#{name}_translations") do
30
+ translations.each_with_object(HashWithIndifferentAccess.new) do |translation, result|
31
+ result[translation.locale] = translation.send(name)
32
+ end
33
+ end
34
+ define_method(:"#{name}_translations=") do |value|
35
+ value.each do |(locale, value)|
36
+ write_attribute name, value, :locale => locale
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,99 @@
1
+ module Translatable
2
+ module ActiveRecord
3
+ module InstanceMethods
4
+ delegate :translated_locales, :to => :translations
5
+
6
+ def translatable
7
+ @translatable ||= Adapter.new(self)
8
+ end
9
+
10
+ def self.included(base)
11
+ # only Rails > 3.1.x compatibility
12
+ base.class_eval %{
13
+ def attributes=(attributes, *args)
14
+ with_given_locale(attributes) { super }
15
+ end
16
+ }
17
+ end
18
+
19
+
20
+ #def write_attribute(name, value, options = {})
21
+ # if translated?(name)
22
+ # options = {:locale => Translatable.locale}.merge(options)
23
+ #
24
+ # # Dirty tracking, paraphrased from
25
+ # # ActiveRecord::AttributeMethods::Dirty#write_attribute.
26
+ # name_str = name.to_s
27
+ # if attribute_changed?(name_str)
28
+ # # If there's already a change, delete it if this undoes the change.
29
+ # old = changed_attributes[name_str]
30
+ # changed_attributes.delete(name_str) if value == old
31
+ # else
32
+ # # If there's not a change yet, record it.
33
+ # old = globalize.fetch(options[:locale], name)
34
+ # old = old.clone if old.duplicable?
35
+ # changed_attributes[name_str] = old if value != old
36
+ # end
37
+ #
38
+ # translatable.write(options[:locale], name, value)
39
+ # else
40
+ # super(name, value)
41
+ # end
42
+ #end
43
+
44
+ def translate
45
+ @translate_me = true
46
+ self
47
+ end
48
+
49
+ def translate?
50
+ !@translate_me.nil? && @translate_me = true
51
+ end
52
+
53
+ def read_attribute(name, options = {})
54
+ options = {:translated => true, :locale => nil}.merge(options)
55
+ if translated?(name) and options[:translated]
56
+ if translate? && (value = translatable.fetch(options[:locale] || Translatable.locale, name))
57
+ value
58
+ else
59
+ super(name)
60
+ end
61
+ else
62
+ super(name)
63
+ end
64
+ end
65
+
66
+ def translated?(name)
67
+ self.class.translated?(name)
68
+ end
69
+
70
+ def translation_for(locale, name, build_if_missing = false)
71
+ unless translation_caches["#{locale}_#{name}"]
72
+ # Fetch translations from database as those in the translation collection may be incomplete
73
+ _translation = translations.detect{|t| t.locale.to_s == locale.to_s && t.key.to_s == name.to_s }
74
+ _translation ||= translations.build(:locale => locale) if build_if_missing
75
+ translation_caches["#{locale}_#{name}"] = _translation if _translation
76
+ end
77
+ translation_caches["#{locale}_#{name}"]
78
+ end
79
+
80
+ def translation_caches
81
+ @translation_caches ||= {}
82
+ end
83
+
84
+
85
+ def with_given_locale(attributes, &block)
86
+ attributes.symbolize_keys! if attributes.respond_to?(:symbolize_keys!)
87
+
88
+ locale = respond_to?(:locale=) ? attributes.try(:[], :locale) :
89
+ attributes.try(:delete, :locale)
90
+
91
+ if locale
92
+ Translatable.with_locale(locale, &block)
93
+ else
94
+ yield
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,96 @@
1
+ module Translatable
2
+ module ActiveRecord
3
+ module Macro
4
+
5
+ def translatable(*attr_names)
6
+
7
+ options = attr_names.extract_options!
8
+ setup_translatable!(options) unless translatable?
9
+
10
+ attr_names = attr_names.map(&:to_sym)
11
+ attr_names -= translated_attribute_names if defined?(translated_attribute_names)
12
+
13
+ if attr_names.present?
14
+ translation_class.instance_eval %{
15
+ attr_accessible :#{attr_names.join(', :')}
16
+ }
17
+
18
+ attr_names.each do |attr_name|
19
+ # Detect and apply serialization.
20
+ serializer = self.serialized_attributes[attr_name.to_s]
21
+ if serializer.present?
22
+ if defined?(::ActiveRecord::Coders::YAMLColumn) &&
23
+ serializer.is_a?(::ActiveRecord::Coders::YAMLColumn)
24
+
25
+ serializer = serializer.object_class
26
+ end
27
+
28
+ translation_class.send :serialize, attr_name, serializer
29
+ end
30
+
31
+ # Create accessors for the attribute.
32
+ translated_attr_accessor(attr_name)
33
+ translations_accessor(attr_name)
34
+
35
+ # Add attribute to the list.
36
+ self.translated_attribute_names << attr_name
37
+ end
38
+
39
+ Translatable.add_translatable self
40
+
41
+ end
42
+ end
43
+
44
+ def class_name
45
+ @class_name ||= begin
46
+ class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].downcase.camelize
47
+ pluralize_table_names ? class_name.singularize : class_name
48
+ end
49
+ end
50
+
51
+ def translatable?
52
+ included_modules.include?(InstanceMethods)
53
+ end
54
+
55
+ protected
56
+ def setup_translatable!(options)
57
+ options[:table_name] ||= Translatable.translation_class.table_name
58
+ options[:foreign_key] ||= 'record_id'
59
+ options[:conditions] ||= ''
60
+
61
+ class_attribute :translated_attribute_names, :translation_options, :fallbacks_for_empty_translations
62
+ self.translated_attribute_names = []
63
+ self.translation_options = options
64
+ self.fallbacks_for_empty_translations = options[:fallbacks_for_empty_translations]
65
+
66
+ # Alias original relation method so we can safely override it in ClassMethods.
67
+ #class << self
68
+ # alias relation_without_globalize relation
69
+ #end
70
+
71
+ include InstanceMethods
72
+ extend ClassMethods
73
+
74
+ translation_class.table_name = options[:table_name]
75
+
76
+ has_many :translations, :class_name => translation_class.name,
77
+ :foreign_key => options[:foreign_key],
78
+ :conditions => options[:conditions],
79
+ :dependent => :destroy,
80
+ :extend => HasManyExtensions,
81
+ :autosave => false
82
+
83
+ #after_create :save_translations!
84
+ #after_update :save_translations!
85
+
86
+ translation_class.instance_eval %{ attr_accessible :lang }
87
+ end
88
+ end
89
+
90
+ module HasManyExtensions
91
+ def find_or_initialize_by_locale(locale)
92
+ with_locale(locale.to_s).first || build(:locale => locale.to_s)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -1,66 +1,13 @@
1
1
  module Translatable
2
2
  module ActiveRecord
3
3
  module Translate
4
- def translate(lang, *attr_names)
5
4
 
6
- model = self.table_name
7
- if Translatable::Config.list.has_key?(model) and self.column_names.include?('group_id')
5
+ # Ask for translations
6
+ def translate
8
7
 
9
- if attr_names.length == 0
10
- to_translate = Translatable::Config.list[model]
11
- q = has_select_values? ? self : self.select("#{model}.*")
12
- else
13
- to_translate = attr_names
14
- q = self
15
- end
16
-
17
- index = 0
18
- to_translate.each do |attr|
19
-
20
- if is_in_scope? attr
21
- index += 1
22
- if Translatable::Config.list[model].include?(attr.to_s)
23
- q = q.joins("LEFT JOIN snippets sn#{index} ON sn#{index}.record_id = #{model}.id AND sn#{index}.scope = '#{model}' AND sn#{index}.group_id = #{model}.group_id AND sn#{index}.lang = '#{lang}' AND sn#{index}.key = '#{attr}'").
24
- select("COALESCE(sn#{index}.value, '') AS #{attr}")
25
- else
26
- q = q.select("#{model}.#{attr}")
27
- end
28
- end
29
-
30
- end
31
-
32
- q.scoped
33
- else
34
- self.scoped
35
- end
36
-
37
- end
38
-
39
- private
40
-
41
- def has_select_values?
42
- self.scoped.select_values.length > 0
43
- end
44
-
45
- def is_in_scope?(attr)
46
-
47
- if has_select_values?
48
- select_values = self.scoped.select_values.map { |one|
49
- if one.is_a?(String)
50
- one.split(',').map(&:lstrip).map(&:rstrip)
51
- elsif one.is_a?(Symbol)
52
- one.to_s
53
- end
54
- }.flatten
55
- is_present = select_values.reject{ |value| !value.is_a?(String) || !value.split('.').last == '*' && !value.split('.').last == attr }
56
- if is_present.nil?
57
- false
58
- else
59
- is_present.length > 0
60
- end
61
- else
62
- true
63
- end
8
+ q = self.scoped
9
+ q.map(&:translate)
10
+ q
64
11
 
65
12
  end
66
13
 
@@ -1,3 +1,8 @@
1
+ require File.expand_path '../active_record/class_methods.rb', __FILE__
2
+ require File.expand_path '../active_record/instance_methods.rb', __FILE__
3
+ require File.expand_path '../active_record/attributes.rb', __FILE__
4
+ require File.expand_path '../active_record/adapter.rb', __FILE__
5
+ require File.expand_path '../active_record/macro.rb', __FILE__
1
6
  require File.expand_path '../active_record/translate.rb', __FILE__
2
7
 
3
8
  module Translatable
@@ -1,57 +1,5 @@
1
- require File.expand_path '../config.rb', __FILE__
1
+ require 'active_record'
2
2
  require File.expand_path '../active_record.rb', __FILE__
3
3
 
4
- module Translatable
5
- class Base
6
-
7
- include Singleton
8
- attr_accessor :models, :clicrdv_models
9
-
10
- CLICRDV_TYPES = %w(model customfield preprosition)
11
-
12
- def self.is_a_customfield?(str)
13
- str.match(/\Acustomfields/)
14
- end
15
-
16
- def self.is_a_preposition?(str)
17
- str.match(/\Apreposition/)
18
- end
19
-
20
- def self.is_an_ar_model?(str)
21
- is_it = false
22
- begin
23
- model = str.singularize.camelize.constantize
24
- is_it = true if model.superclass.name == 'ActiveRecord::Base' && str != 'customfields'
25
- rescue NameError
26
- end
27
- is_it
28
- end
29
-
30
- def self.type str
31
- if is_model str
32
- 'model'
33
- elsif is_customfield str
34
- 'customfield'
35
- end
36
- end
37
-
38
- # Get all translatables models
39
- # @param array of things to translate
40
- # @return an array of three elements : ARModels, Customfields and Preprositions
41
- def self.clicrdv_models(hash=nil)
42
- if hash.nil? || hash.empty?
43
- process_me = Config.list
44
- else
45
- process_me = hash
46
- end
47
- # Don't remove the v in the block parameters |k,v| this because of Ruby 1.8.7
48
- ar_models = process_me.reject{ |k,v| !is_an_ar_model?(k) }
49
- customfields = process_me.reject{ |k,v| !is_a_customfield?(k) }
50
- prepositions = process_me.reject{ |k,v| !is_a_preposition?(k) }
51
-
52
- [ar_models, customfields, prepositions]
53
- end
54
- end
55
- end
56
-
57
- ActiveRecord::Base.extend(Translatable::ActiveRecord::Translate)
4
+ ActiveRecord::Base.extend(Translatable::ActiveRecord::Translate)
5
+ ActiveRecord::Base.extend(Translatable::ActiveRecord::Macro)
@@ -1,3 +1,3 @@
1
1
  module Translatable
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/translatable.rb CHANGED
@@ -1 +1,58 @@
1
1
  require File.join(File.dirname(__FILE__), 'translatable/default.rb')
2
+
3
+ module Translatable
4
+
5
+ @@translatable ||= Hash.new
6
+ @@translation_class ||= nil
7
+
8
+ class << self
9
+ def locale
10
+ I18n.locale
11
+ end
12
+
13
+ def locale=(locale)
14
+ set_locale(locale)
15
+ end
16
+
17
+ def with_locale(locale, &block)
18
+ previous_locale = read_locale
19
+ set_locale(locale)
20
+ result = yield(locale)
21
+ set_locale(previous_locale)
22
+ result
23
+ end
24
+
25
+ def translation_class_name=(klass)
26
+ @@translation_class = klass.constantize
27
+ end
28
+ def translation_class
29
+ @@translation_class ||= nil
30
+ end
31
+
32
+ # Hash of models that are translatable (values are the attrs)
33
+ def list
34
+ @@translatable ||= Hash.new
35
+ end
36
+
37
+ def add_translatable(klass)
38
+ if @@translatable.has_key? klass.name
39
+ klass.translated_attribute_names.each do |attr|
40
+ @@translatable[klass.name] << attr unless @@translatable[klass.name].include?(attr)
41
+ end
42
+ else
43
+ @@translatable[klass.name] = klass.translated_attribute_names
44
+ end
45
+ end
46
+
47
+ protected
48
+
49
+ def read_locale
50
+ Thread.current[:translatable_locale]
51
+ end
52
+
53
+ def set_locale(locale)
54
+ Thread.current[:translatable_locale] = locale.try(:to_sym)
55
+ end
56
+ end
57
+
58
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy_translatable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Bonaud
@@ -60,10 +60,15 @@ executables: []
60
60
  extensions: []
61
61
  extra_rdoc_files: []
62
62
  files:
63
+ - README.md
63
64
  - lib/translatable.rb
64
65
  - lib/translatable/active_record.rb
66
+ - lib/translatable/active_record/adapter.rb
67
+ - lib/translatable/active_record/attributes.rb
68
+ - lib/translatable/active_record/class_methods.rb
69
+ - lib/translatable/active_record/instance_methods.rb
70
+ - lib/translatable/active_record/macro.rb
65
71
  - lib/translatable/active_record/translate.rb
66
- - lib/translatable/config.rb
67
72
  - lib/translatable/default.rb
68
73
  - lib/translatable/version.rb
69
74
  homepage: http://rubygems.org/gems/single_translatable
@@ -1,14 +0,0 @@
1
- module Translatable
2
- class Config
3
- include Singleton
4
- attr_accessor :list
5
-
6
- def self.file= path
7
- instance.list = YAML.load_file path
8
- end
9
-
10
- def self.list
11
- instance.list
12
- end
13
- end
14
- end