attr_translate 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7d9a44a5c74b80f11fc6c36cc4153bec6f019bb3
4
+ data.tar.gz: 4716ef9f800a4786ed938a84432ad1c729aba82e
5
+ SHA512:
6
+ metadata.gz: c3a9a0bb4c6b23f8e8571de91eeeb416f5df2b059b5147f617d94f8d0a07cb113b007d78853fbd0a268d51f1ca1a34b4e74e9d82974df639a6b9b3352c1c9d19
7
+ data.tar.gz: daf05ddcc2a0d73f6e7161b851b9e071a2f88a982cffeb3e5cd81dc9ce401401dd4c4f9be13468763bcde5cbaab12f3b686fbb31be35d62c2011b73ecb491d24
@@ -0,0 +1,4 @@
1
+ # Change Log
2
+
3
+ ## v1.0.0
4
+ - Initial release.
@@ -0,0 +1,83 @@
1
+ # AttrTranslate
2
+
3
+ Rails concern for ActiveRecord attribute translation using PostgreSQL's JSONB datatype.
4
+
5
+ AttrTranslate is extracted from the Brightcommerce platform and is used in a number of other software projects.
6
+
7
+ ## Installation
8
+
9
+ To install add the line to your Gemfile:
10
+
11
+ ``` ruby
12
+ gem 'attr_translate'
13
+ ```
14
+
15
+ And `bundle install`.
16
+
17
+ ## How To Use
18
+
19
+ To add AttrTranslate to a model, include the concern and class method:
20
+
21
+ ``` ruby
22
+ class Post < ActiveRecord::Base
23
+ include AttrTranslate
24
+
25
+ attr_translate :title, :body
26
+ end
27
+ ```
28
+
29
+ For convenience the `attr_translate` class method is aliased internally as `attr_translates`.
30
+
31
+ To autoload AttrTranslate for all models, add the following to an initializer:
32
+
33
+ ``` ruby
34
+ require 'attr_translate/active_record'
35
+ ```
36
+
37
+ You then don't need to `include AttrTranslate` in any model, but you still need to add the `attr_translate` class method.
38
+
39
+ ### Setup
40
+
41
+ Each attribute that will have translations will need to be setup appropriately in your model's migration. Append `_translations` to the column name, and set the column type to `:jsonb`. To aid in search, setup an index using the `gin` index type. Each translation will be stored using the locale as a key in a hash and converted to JSON.
42
+
43
+ ``` ruby
44
+ class CreatePosts < ActiveRecord::Migration[5.2]
45
+ def change
46
+ create_table :posts do |table|
47
+ table.column :title_translations, :jsonb, default: {}, index: {using: 'gin'}
48
+ table.column :body_translations, :jsonb, default: {}, index: {using: 'gin'}
49
+ table.timestamps
50
+ end
51
+ end
52
+ end
53
+ ```
54
+
55
+ ## Dependencies
56
+
57
+ AttrTranslate gem has the following runtime dependencies:
58
+ - activerecord >= 5.1.4
59
+ - activesupport >= 5.1.4
60
+
61
+ ## Compatibility
62
+
63
+ Tested with MRI 2.4.2 against Rails 5.2.2.
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create new Pull Request
72
+
73
+ ## Credit
74
+
75
+ This gem was written and is maintained by [Jurgen Jocubeit](https://github.com/JurgenJocubeit), CEO and President Brightcommerce, Inc.
76
+
77
+ ## License
78
+
79
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
80
+
81
+ ## Copyright
82
+
83
+ Copyright 2018 Brightcommerce, Inc.
@@ -0,0 +1,2 @@
1
+ require 'attr_translate/attr_translate'
2
+ require 'attr_translate/version'
@@ -0,0 +1 @@
1
+ ActiveRecord::Base.send :include, AttrTranslate
@@ -0,0 +1,145 @@
1
+ require 'active_support/concern'
2
+ require_relative './translation_attribute'
3
+
4
+ module AttrTranslate
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ def attr_translate(*attrs)
9
+ class_attribute :translated_attribute_names, :permitted_translated_attributes
10
+
11
+ self.translated_attribute_names = attrs
12
+ self.permitted_translated_attributes = [
13
+ *self.ancestors
14
+ .select { |klass| klass.respond_to?(:permitted_translated_attributes) }
15
+ .map(&:permitted_translated_attributes),
16
+ *attrs.product(I18n.available_locales)
17
+ .map { |attribute, locale| :"#{attribute}_#{locale}" }
18
+ ].flatten.compact
19
+
20
+ attrs.each do |attr_name|
21
+ define_method attr_name do |**params|
22
+ read_json_translation(attr_name, params)
23
+ end
24
+
25
+ define_method "#{attr_name}=" do |value|
26
+ write_json_translation(attr_name, value)
27
+ end
28
+
29
+ define_singleton_method "with_#{attr_name}_translation" do |value, locale = I18n.locale|
30
+ quoted_translation_store = connection.quote_column_name("#{attr_name}_translations")
31
+ translation_hash = { "#{locale}" => value }
32
+ where("#{quoted_translation_store} @> :translation::jsonb", translation: translation_hash.to_json)
33
+ end
34
+ end
35
+
36
+ send(:prepend, TranslationAttribute)
37
+ end
38
+ alias_method :attr_translates, :attr_translate
39
+
40
+ def translates?
41
+ true
42
+ end
43
+
44
+ end
45
+
46
+ def disable_fallback
47
+ toggle_fallback(false)
48
+ end
49
+
50
+ def enable_fallback
51
+ toggle_fallback(true)
52
+ end
53
+
54
+ protected
55
+
56
+ attr_reader :enabled_fallback
57
+
58
+ def json_translate_fallback_locales(locale)
59
+ return locale if enabled_fallback == false || !::I18n.respond_to?(:fallbacks)
60
+ ::I18n.fallbacks[locale]
61
+ end
62
+
63
+ def read_json_translation(attr_name, locale = I18n.locale, **params)
64
+ translations = public_send("#{attr_name}_translations") || {}
65
+
66
+ available = Array(json_translate_fallback_locales(locale)).detect do |available_locale|
67
+ translations[available_locale.to_s].present?
68
+ end
69
+
70
+ translation = translations[available.to_s]
71
+ # Rescue from MissingInterpolationArgument
72
+ # so the default behaviour doesn't change.
73
+ begin
74
+ ::I18n.interpolate(translation, params) if translation
75
+ rescue ::I18n::MissingInterpolationArgument
76
+ translation
77
+ end
78
+ end
79
+
80
+ def write_json_translation(attr_name, value, locale = I18n.locale)
81
+ translation_store = "#{attr_name}_translations"
82
+ translations = public_send(translation_store) || {}
83
+ public_send("#{translation_store}_will_change!") unless translations[locale.to_s] == value
84
+ translations[locale.to_s] = value
85
+ public_send("#{translation_store}=", translations)
86
+ value
87
+ end
88
+
89
+ def respond_to_with_translates?(symbol, include_all = false)
90
+ return true if parse_translated_attribute_accessor(symbol)
91
+ respond_to_without_translates?(symbol, include_all)
92
+ end
93
+
94
+ def method_missing_with_translates(method_name, *args)
95
+ translated_attr_name, locale, assigning = parse_translated_attribute_accessor(method_name)
96
+
97
+ return method_missing_without_translates(method_name, *args) unless translated_attr_name
98
+
99
+ if assigning
100
+ write_json_translation(translated_attr_name, args.first, locale)
101
+ else
102
+ read_json_translation(translated_attr_name, locale)
103
+ end
104
+ end
105
+
106
+ # Internal: Parse a translated convenience accessor name.
107
+ #
108
+ # method_name - The accessor name.
109
+ #
110
+ # Examples
111
+ #
112
+ # parse_translated_attribute_accessor("title_en=")
113
+ # # => [:title, :en, true]
114
+ #
115
+ # parse_translated_attribute_accessor("title_fr")
116
+ # # => [:title, :fr, false]
117
+ #
118
+ # Returns the attribute name Symbol, locale Symbol, and a Boolean
119
+ # indicating whether or not the caller is attempting to assign a value.
120
+ def parse_translated_attribute_accessor(method_name)
121
+ return unless /\A(?<attribute>[a-z0-9_]+)_(?<locale>[a-z]{2})(?<assignment>=?)\z/ =~ method_name
122
+
123
+ translated_attr_name = attribute.to_sym
124
+ return unless translated_attribute_names.include?(translated_attr_name)
125
+
126
+ locale = locale.to_sym
127
+ assigning = assignment.present?
128
+
129
+ [translated_attr_name, locale, assigning]
130
+ end
131
+
132
+ def toggle_fallback(enabled)
133
+ if block_given?
134
+ old_value = @enabled_fallback
135
+ begin
136
+ @enabled_fallback = enabled
137
+ yield
138
+ ensure
139
+ @enabled_fallback = old_value
140
+ end
141
+ else
142
+ @enabled_fallback = enabled
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,18 @@
1
+ module TranslationAttribute
2
+ def respond_to?(symbol, include_all = false)
3
+ return true if parse_translated_attribute_accessor(symbol)
4
+ super(symbol, include_all)
5
+ end
6
+
7
+ def method_missing(method_name, *args, **params)
8
+ translated_attr_name, locale, assigning = parse_translated_attribute_accessor(method_name)
9
+
10
+ return super(method_name, *args, **params) unless translated_attr_name
11
+
12
+ if assigning
13
+ write_json_translation(translated_attr_name, args.first, locale)
14
+ else
15
+ read_json_translation(translated_attr_name, locale, **params)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module AttrTranslate
2
+ module Version
3
+ Major = 1
4
+ Minor = 0
5
+ Revision = 0
6
+ Prerelease = nil
7
+ Compact = [Major, Minor, Revision, Prerelease].compact.join('.')
8
+ Summary = "AttrTranslate v#{Compact}"
9
+ Description = "Provides scopes for finding published, unpublished and returning recent or upcoming items."
10
+ Author = "Jurgen Jocubeit"
11
+ Email = "support@brightcommerce.com"
12
+ Homepage = "https://github.com/brightcommerce/attr_translate"
13
+ Metadata = {'copyright' => 'Copyright 2018 Brightcommerce, Inc. All Rights Reserved.'}
14
+ License = "MIT"
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: attr_translate
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jurgen Jocubeit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 5.1.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 5.1.4
41
+ description: Provides scopes for finding published, unpublished and returning recent
42
+ or upcoming items.
43
+ email: support@brightcommerce.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - CHANGELOG.md
49
+ - README.md
50
+ - lib/attr_translate.rb
51
+ - lib/attr_translate/active_record.rb
52
+ - lib/attr_translate/attr_translate.rb
53
+ - lib/attr_translate/translation_attribute.rb
54
+ - lib/attr_translate/version.rb
55
+ homepage: https://github.com/brightcommerce/attr_translate
56
+ licenses:
57
+ - MIT
58
+ metadata:
59
+ copyright: Copyright 2018 Brightcommerce, Inc. All Rights Reserved.
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '2.3'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.6.13
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: AttrTranslate v1.0.0
80
+ test_files: []