easy_translatable 0.0.7 → 0.1.0

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.
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