jsonb_translate 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4570aecfea3653152fe8dbebf4aea49354b4b7f9
4
+ data.tar.gz: 4a8947e3e7fb901aa73f443373e7d2c6c79c537b
5
+ SHA512:
6
+ metadata.gz: d251bfe5c9220d3177cb67157b7c15f0a5d4fdcb8100a4cb565831c84991e82250ea12250449c64f5f8eeca6b0121bf49d23f959685cac5f8d4807d2e0212bcb
7
+ data.tar.gz: 484b5770f2e215b2060d2cd72d49f2aa42d0548c1dea1df4c02df6a3ec19c9a2e1d7044276bce3753e9067debc37d0a4a0f7095c02f8a2c304ae6875d718fa4f
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Rob Worley
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,135 @@
1
+ # jsonb_translate
2
+
3
+ Fork of [hstore-translate](https://github.com/Leadformance/hstore_translate/)
4
+
5
+ Rails I18n library for ActiveRecord model/data translation using PostgreSQL's
6
+ jsonb datatype. It provides an interface inspired by
7
+ [Globalize3](https://github.com/svenfuchs/globalize3) but removes the need to
8
+ maintain separate translation tables.
9
+
10
+ ## Requirements
11
+
12
+ * ActiveRecord > 4.2.0
13
+ * I18n
14
+
15
+ ## Installation
16
+
17
+ gem install jsonb_translate
18
+
19
+ When using bundler, put it in your Gemfile:
20
+
21
+ ```ruby
22
+ source 'https://rubygems.org'
23
+
24
+ gem 'activerecord'
25
+ gem 'pg', :platform => :ruby
26
+ gem 'jsonb_translate'
27
+ ```
28
+
29
+ ## Model translations
30
+
31
+ Model translations allow you to translate your models' attribute values. E.g.
32
+
33
+ ```ruby
34
+ class Post < ActiveRecord::Base
35
+ translates :title, :body
36
+ end
37
+ ```
38
+
39
+ Allows you to translate the attributes :title and :body per locale:
40
+
41
+ ```ruby
42
+ I18n.locale = :en
43
+ post.title # => This database rocks!
44
+
45
+ I18n.locale = :he
46
+ post.title # => אתר זה טוב
47
+ ```
48
+
49
+ You also have locale-specific convenience methods from [easy_globalize3_accessors](https://github.com/paneq/easy_globalize3_accessors):
50
+
51
+ ```ruby
52
+ I18n.locale = :en
53
+ post.title # => This database rocks!
54
+ post.title_he # => אתר זה טוב
55
+ ```
56
+
57
+ To find records using translations without constructing hstore queries by hand:
58
+
59
+ ```ruby
60
+ Post.with_title_translation("This database rocks!") # => #<ActiveRecord::Relation ...>
61
+ Post.with_title_translation("אתר זה טוב", :he) # => #<ActiveRecord::Relation ...>
62
+ ```
63
+
64
+ In order to make this work, you'll need to define an hstore column for each of
65
+ your translated attributes, using the suffix "_translations":
66
+
67
+ ```ruby
68
+ class CreatePosts < ActiveRecord::Migration
69
+ def up
70
+ create_table :posts do |t|
71
+ t.jsonb :title_translations
72
+ t.jsonb :body_translations
73
+ t.timestamps
74
+ end
75
+ end
76
+ def down
77
+ drop_table :posts
78
+ end
79
+ end
80
+ ```
81
+
82
+ ## I18n fallbacks for missing translations
83
+
84
+ It is possible to enable fallbacks for missing translations. It will depend
85
+ on the configuration setting you have set for I18n translations in your Rails
86
+ config.
87
+
88
+ You can enable them by adding the next line to `config/application.rb` (or
89
+ only `config/environments/production.rb` if you only want them in production)
90
+
91
+ ```ruby
92
+ config.i18n.fallbacks = true
93
+ ```
94
+
95
+ Sven Fuchs wrote a [detailed explanation of the fallback
96
+ mechanism](https://github.com/svenfuchs/i18n/wiki/Fallbacks).
97
+
98
+ ## Temporarily disable fallbacks
99
+
100
+ If you've enabled fallbacks for missing translations, you probably want to disable
101
+ them in the admin interface to display which translations the user still has to
102
+ fill in.
103
+
104
+ From:
105
+
106
+ ```ruby
107
+ I18n.locale = :en
108
+ post.title # => This database rocks!
109
+ post.title_nl # => This database rocks!
110
+ ```
111
+
112
+ To:
113
+
114
+ ```ruby
115
+ I18n.locale = :en
116
+ post.title # => This database rocks!
117
+ post.disable_fallback
118
+ post.title_nl # => nil
119
+ ```
120
+
121
+ You can also call your code into a block that temporarily disable or enable fallbacks.
122
+
123
+ ```ruby
124
+ I18n.locale = :en
125
+ post.title_nl # => This database rocks!
126
+
127
+ post.disable_fallback do
128
+ post.title_nl # => nil
129
+ end
130
+
131
+ post.disable_fallback
132
+ post.enable_fallback do
133
+ post.title_nl # => This database rocks!
134
+ end
135
+ ```
@@ -0,0 +1,9 @@
1
+ require 'active_record'
2
+ require 'active_record/connection_adapters/postgresql_adapter'
3
+ require 'jsonb_translate/translates'
4
+
5
+ module JsonbTranslate
6
+ end
7
+
8
+ ActiveRecord::Base.extend(JsonbTranslate::Translates)
9
+
@@ -0,0 +1,136 @@
1
+ module JsonbTranslate
2
+ module Translates
3
+ def translates(*attrs)
4
+ include InstanceMethods
5
+
6
+ class_attribute :translated_attrs
7
+ alias_attribute :translated_attribute_names, :translated_attrs # Improve compatibility with the gem globalize
8
+ self.translated_attrs = attrs
9
+
10
+ attrs.each do |attr_name|
11
+ define_method attr_name do
12
+ read_jsonb_translation(attr_name)
13
+ end
14
+
15
+ define_method "#{attr_name}=" do |value|
16
+ write_jsonb_translation(attr_name, value)
17
+ end
18
+
19
+ define_singleton_method "with_#{attr_name}_translation" do |value, locale = I18n.locale|
20
+ quoted_translation_store = connection.quote_column_name("#{attr_name}_translations")
21
+ q = {}
22
+ q[locale] = value
23
+ where("#{quoted_translation_store} @> ?", q.to_json)
24
+ end
25
+ end
26
+
27
+ alias_method_chain :respond_to?, :translates
28
+ alias_method_chain :method_missing, :translates
29
+ end
30
+
31
+ # Improve compatibility with the gem globalize
32
+ def translates?
33
+ included_modules.include?(InstanceMethods)
34
+ end
35
+
36
+ module InstanceMethods
37
+ def disable_fallback(&block)
38
+ toggle_fallback(enabled = false, &block)
39
+ end
40
+
41
+ def enable_fallback(&block)
42
+ toggle_fallback(enabled = true, &block)
43
+ end
44
+
45
+ protected
46
+
47
+ def jsonb_translate_fallback_locales(locale)
48
+ return if @enabled_fallback == false || !I18n.respond_to?(:fallbacks)
49
+ I18n.fallbacks[locale]
50
+ end
51
+
52
+ def read_jsonb_translation(attr_name, locale = I18n.locale)
53
+ translations = send("#{attr_name}_translations") || {}
54
+ translation = translations[locale.to_s]
55
+
56
+ if fallback_locales = jsonb_translate_fallback_locales(locale)
57
+ fallback_locales.each do |fallback_locale|
58
+ t = translations[fallback_locale.to_s]
59
+ if t && !t.empty? # differs from blank?
60
+ translation = t
61
+ break
62
+ end
63
+ end
64
+ end
65
+
66
+ translation
67
+ end
68
+
69
+ def write_jsonb_translation(attr_name, value, locale = I18n.locale)
70
+ translation_store = "#{attr_name}_translations"
71
+ translations = send(translation_store) || {}
72
+ send("#{translation_store}_will_change!") unless translations[locale.to_s] == value
73
+ translations[locale.to_s] = value
74
+ send("#{translation_store}=", translations)
75
+ value
76
+ end
77
+
78
+ def respond_to_with_translates?(symbol, include_all = false)
79
+ return true if parse_translated_attribute_accessor(symbol)
80
+ respond_to_without_translates?(symbol, include_all)
81
+ end
82
+
83
+ def method_missing_with_translates(method_name, *args)
84
+ translated_attr_name, locale, assigning = parse_translated_attribute_accessor(method_name)
85
+
86
+ return method_missing_without_translates(method_name, *args) unless translated_attr_name
87
+
88
+ if assigning
89
+ write_jsonb_translation(translated_attr_name, args.first, locale)
90
+ else
91
+ read_jsonb_translation(translated_attr_name, locale)
92
+ end
93
+ end
94
+
95
+ # Internal: Parse a translated convenience accessor name.
96
+ #
97
+ # method_name - The accessor name.
98
+ #
99
+ # Examples
100
+ #
101
+ # parse_translated_attribute_accessor("title_en=")
102
+ # # => [:title, :en, true]
103
+ #
104
+ # parse_translated_attribute_accessor("title_fr")
105
+ # # => [:title, :fr, false]
106
+ #
107
+ # Returns the attribute name Symbol, locale Symbol, and a Boolean
108
+ # indicating whether or not the caller is attempting to assign a value.
109
+ def parse_translated_attribute_accessor(method_name)
110
+ return unless method_name =~ /\A([a-z_]+)_([a-z]{2})(=?)\z/
111
+
112
+ translated_attr_name = $1.to_sym
113
+ return unless translated_attrs.include?(translated_attr_name)
114
+
115
+ locale = $2.to_sym
116
+ assigning = $3.present?
117
+
118
+ [translated_attr_name, locale, assigning]
119
+ end
120
+
121
+ def toggle_fallback(enabled, &block)
122
+ if block_given?
123
+ old_value = @enabled_fallback
124
+ begin
125
+ @enabled_fallback = enabled
126
+ yield
127
+ ensure
128
+ @enabled_fallback = old_value
129
+ end
130
+ else
131
+ @enabled_fallback = enabled
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,3 @@
1
+ module JsonbTranslate
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,9 @@
1
+ test:
2
+ adapter: postgresql
3
+ encoding: unicode
4
+ database: translate
5
+ pool: 5
6
+ host: 'localhost'
7
+ username: translate
8
+ password: translate
9
+ template: template0
@@ -0,0 +1,59 @@
1
+ require 'minitest/autorun'
2
+ require 'jsonb_translate'
3
+
4
+ require 'database_cleaner'
5
+ DatabaseCleaner.strategy = :transaction
6
+
7
+ MiniTest::Test = MiniTest::Unit::TestCase unless MiniTest.const_defined?(:Test) # Rails 4.0.x
8
+
9
+ class Post < ActiveRecord::Base
10
+ translates :title
11
+ end
12
+
13
+ class JsonbTranslate::Test < Minitest::Test
14
+ class << self
15
+ def prepare_database
16
+ create_database
17
+ create_table
18
+ end
19
+
20
+ private
21
+
22
+ def db_config
23
+ @db_config ||= begin
24
+ filepath = File.join('test', 'database.yml')
25
+ YAML.load_file(filepath)['test']
26
+ end
27
+ end
28
+
29
+ def establish_connection(config)
30
+ ActiveRecord::Base.establish_connection(config)
31
+ ActiveRecord::Base.connection
32
+ end
33
+
34
+ def create_database
35
+ system_config = db_config.merge('database' => 'postgres', 'schema_search_path' => 'public')
36
+ connection = establish_connection(system_config)
37
+ connection.create_database(db_config['database']) rescue nil
38
+ end
39
+
40
+ def create_table
41
+ connection = establish_connection(db_config)
42
+ connection.create_table(:posts, :force => true) do |t|
43
+ t.column :title_translations, 'jsonb'
44
+ end
45
+ end
46
+ end
47
+
48
+ prepare_database
49
+
50
+ def setup
51
+ I18n.available_locales = ['en', 'en-US', 'fr']
52
+ I18n.config.enforce_available_locales = true
53
+ DatabaseCleaner.start
54
+ end
55
+
56
+ def teardown
57
+ DatabaseCleaner.clean
58
+ end
59
+ end
@@ -0,0 +1,147 @@
1
+ require 'test_helper'
2
+
3
+ class TranslatesTest < JsonbTranslate::Test
4
+ def test_assigns_in_current_locale
5
+ I18n.with_locale(:en) do
6
+ p = Post.new(:title => "English Title")
7
+ assert_equal("English Title", p.title_translations['en'])
8
+ end
9
+ end
10
+
11
+ def test_retrieves_in_current_locale
12
+ p = Post.new(:title_translations => { "en" => "English Title", "fr" => "Titre français" })
13
+ I18n.with_locale(:fr) do
14
+ assert_equal("Titre français", p.title)
15
+ end
16
+ end
17
+
18
+ def test_retrieves_in_current_locale_with_fallbacks
19
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
20
+ I18n.default_locale = :"en-US"
21
+
22
+ p = Post.new(:title_translations => {"en" => "English Title"})
23
+ I18n.with_locale(:fr) do
24
+ assert_equal("English Title", p.title)
25
+ end
26
+ end
27
+
28
+ def test_assigns_in_specified_locale
29
+ I18n.with_locale(:en) do
30
+ p = Post.new(:title_translations => { "en" => "English Title" })
31
+ p.title_fr = "Titre français"
32
+ assert_equal("Titre français", p.title_translations["fr"])
33
+ end
34
+ end
35
+
36
+ def test_persists_changes_in_specified_locale
37
+ I18n.with_locale(:en) do
38
+ p = Post.create!(:title_translations => { "en" => "Original Text" })
39
+ p.title_en = "Updated Text"
40
+ p.save!
41
+ assert_equal("Updated Text", Post.last.title_en)
42
+ end
43
+ end
44
+
45
+ def test_retrieves_in_specified_locale
46
+ I18n.with_locale(:en) do
47
+ p = Post.new(:title_translations => { "en" => "English Title", "fr" => "Titre français" })
48
+ assert_equal("Titre français", p.title_fr)
49
+ end
50
+ end
51
+
52
+ def test_retrieves_in_specified_locale_with_fallbacks
53
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
54
+ I18n.default_locale = :"en-US"
55
+
56
+ p = Post.new(:title_translations => { "en" => "English Title" })
57
+ I18n.with_locale(:fr) do
58
+ assert_equal("English Title", p.title_fr)
59
+ end
60
+ end
61
+
62
+ def test_fallback_from_empty_string
63
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
64
+ I18n.default_locale = :"en-US"
65
+
66
+ p = Post.new(:title_translations => { "en" => "English Title", "fr" => "" })
67
+ I18n.with_locale(:fr) do
68
+ assert_equal("English Title", p.title_fr)
69
+ end
70
+ end
71
+
72
+ def test_retrieves_in_specified_locale_with_fallback_disabled
73
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
74
+ I18n.default_locale = :"en-US"
75
+
76
+ p = Post.new(:title_translations => { "en" => "English Title" })
77
+ p.disable_fallback
78
+ I18n.with_locale(:fr) do
79
+ assert_equal(nil, p.title_fr)
80
+ end
81
+ end
82
+
83
+ def test_retrieves_in_specified_locale_with_fallback_disabled_using_a_block
84
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
85
+ I18n.default_locale = :"en-US"
86
+
87
+ p = Post.new(:title_translations => { "en" => "English Title" })
88
+ p.enable_fallback
89
+
90
+ assert_equal("English Title", p.title_fr)
91
+ p.disable_fallback { assert_nil p.title_fr }
92
+ end
93
+
94
+ def test_retrieves_in_specified_locale_with_fallback_reenabled
95
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
96
+ I18n.default_locale = :"en-US"
97
+
98
+ p = Post.new(:title_translations => { "en" => "English Title" })
99
+ p.disable_fallback
100
+ p.enable_fallback
101
+ I18n.with_locale(:fr) do
102
+ assert_equal("English Title", p.title_fr)
103
+ end
104
+ end
105
+
106
+ def test_retrieves_in_specified_locale_with_fallback_reenabled_using_a_block
107
+ I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
108
+ I18n.default_locale = :"en-US"
109
+
110
+ p = Post.new(:title_translations => { "en" => "English Title" })
111
+ p.disable_fallback
112
+
113
+ assert_nil(p.title_fr)
114
+ p.enable_fallback { assert_equal("English Title", p.title_fr) }
115
+ end
116
+
117
+ def test_method_missing_delegates
118
+ assert_raises(NoMethodError) { Post.new.nonexistant_method }
119
+ end
120
+
121
+ def test_method_missing_delegates_non_translated_attributes
122
+ assert_raises(NoMethodError) { Post.new.other_fr }
123
+ end
124
+
125
+ def test_persists_translations_assigned_as_hash
126
+ p = Post.create!(:title_translations => { "en" => "English Title", "fr" => "Titre français" })
127
+ p.reload
128
+ assert_equal({"en" => "English Title", "fr" => "Titre français"}, p.title_translations)
129
+ end
130
+
131
+ def test_persists_translations_assigned_to_localized_accessors
132
+ p = Post.create!(:title_en => "English Title", :title_fr => "Titre français")
133
+ p.reload
134
+ assert_equal({"en" => "English Title", "fr" => "Titre français"}, p.title_translations)
135
+ end
136
+
137
+ def test_with_translation_relation
138
+ p = Post.create!(:title_translations => { "en" => "Alice in Wonderland", "fr" => "Alice au pays des merveilles" })
139
+ I18n.with_locale(:en) do
140
+ assert_equal p.title_en, Post.with_title_translation("Alice in Wonderland").first.try(:title)
141
+ end
142
+ end
143
+
144
+ def test_class_method_translates?
145
+ assert_equal true, Post.translates?
146
+ end
147
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsonb_translate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gleb Tv
8
+ - Rob Worley
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-02-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '4.2'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '4.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: pg
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: bundler
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '4.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '4.0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: database_cleaner
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ description: Rails I18n library for ActiveRecord model/data translation using PostgreSQL's
99
+ jsonb datatype. Translations are stored directly in the model table rather than
100
+ shadow tables.
101
+ email: glebtv@gmail.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - MIT-LICENSE
107
+ - README.md
108
+ - lib/jsonb_translate.rb
109
+ - lib/jsonb_translate/translates.rb
110
+ - lib/jsonb_translate/version.rb
111
+ - test/database.yml
112
+ - test/test_helper.rb
113
+ - test/translates_test.rb
114
+ homepage: https://github.com/glebtv/jsonb_translate
115
+ licenses:
116
+ - MIT
117
+ metadata: {}
118
+ post_install_message:
119
+ rdoc_options: []
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 2.4.5.1
135
+ signing_key:
136
+ specification_version: 4
137
+ summary: Rails I18n library for ActiveRecord model/data translation using PostgreSQL's
138
+ jsonb datatype.
139
+ test_files:
140
+ - test/database.yml
141
+ - test/translates_test.rb
142
+ - test/test_helper.rb