translateable 0.2.2 → 0.3.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 +4 -4
- data/.github/workflows/tests.yml +10 -18
- data/README.md +51 -49
- data/gemfiles/7.0.gemfile +1 -0
- data/gemfiles/7.1.gemfile +1 -0
- data/gemfiles/7.2.gemfile +1 -0
- data/gemfiles/8.0.gemfile +1 -0
- data/gemfiles/8.1.gemfile +7 -0
- data/lib/generators/translateable/templates/migration.rb.erb +3 -3
- data/lib/translateable/version.rb +1 -1
- data/lib/translateable.rb +27 -19
- data/translateable.gemspec +3 -2
- metadata +20 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aeb41f65842e0bad160968db1710b45da49c12619b6e705526014fa99038bbf3
|
|
4
|
+
data.tar.gz: fddb74f188f3ecfe4d9abe94a43199f019510d8deb64fbba08706c1e449e632f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fdcbcbd924443fac434c66ea35a8df53928c61a4e58d83868d74c6711195457c07c4619cb324ec47d0ace9f047168a10d17f43eebbb0dc6d81934d422a7aa5b6
|
|
7
|
+
data.tar.gz: dc66c4efaf4ec49d590bf739b09b71bea0dc583e60709520634d2b9314d1ac4975275365570a975c8288ac9f0c4065a44e99793297444465a699a045b2244b08
|
data/.github/workflows/tests.yml
CHANGED
|
@@ -11,38 +11,30 @@ jobs:
|
|
|
11
11
|
image: postgres
|
|
12
12
|
env:
|
|
13
13
|
POSTGRES_PASSWORD: postgres
|
|
14
|
-
# Set health checks to wait until postgres has started
|
|
15
14
|
options: >-
|
|
16
15
|
--health-cmd pg_isready
|
|
17
16
|
--health-interval 10s
|
|
18
17
|
--health-timeout 5s
|
|
19
18
|
--health-retries 5
|
|
20
19
|
ports:
|
|
21
|
-
# Maps tcp port 5432 on service container to the host
|
|
22
20
|
- 5432:5432
|
|
23
21
|
|
|
24
22
|
strategy:
|
|
25
23
|
fail-fast: false
|
|
26
24
|
matrix:
|
|
27
|
-
ruby-version: ['3.2', '3.3', '3.4']
|
|
28
|
-
gemfile: ['7.0', '7.1', '7.2', '8.0']
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
ruby-version: ['3.2', '3.3', '3.4', '4.0']
|
|
26
|
+
gemfile: ['7.0', '7.1', '7.2', '8.0', '8.1']
|
|
27
|
+
|
|
28
|
+
env:
|
|
29
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
|
30
|
+
POSTGRES_HOST: localhost
|
|
31
|
+
POSTGRES_PORT: 5432
|
|
32
|
+
POSTGRES_PASSWORD: postgres
|
|
32
33
|
|
|
33
34
|
steps:
|
|
34
|
-
- uses: actions/checkout@
|
|
35
|
+
- uses: actions/checkout@v7
|
|
35
36
|
- uses: ruby/setup-ruby@v1
|
|
36
37
|
with:
|
|
37
38
|
ruby-version: ${{ matrix.ruby-version }}
|
|
38
|
-
|
|
39
|
-
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
|
|
40
|
-
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
|
39
|
+
bundler-cache: true
|
|
41
40
|
- run: bundle exec rspec
|
|
42
|
-
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
|
|
43
|
-
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
|
44
|
-
# The hostname used to communicate with the PostgreSQL service container
|
|
45
|
-
POSTGRES_HOST: localhost
|
|
46
|
-
# The default PostgreSQL port
|
|
47
|
-
POSTGRES_PORT: 5432
|
|
48
|
-
POSTGRES_PASSWORD: postgres
|
data/README.md
CHANGED
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
Allows you to store text data in multiple languages with your ActiveRecord models. Similar to [globalize](https://github.com/globalize/globalize), but with a few differences:
|
|
7
7
|
|
|
8
|
-
1.
|
|
9
|
-
2.
|
|
10
|
-
3. Provides easy integration with forms using nested attributes so you can create records with multiple translations in one form. Together with [nested_form_fields](https://github.com/ncri/nested_form_fields) you can dynamically add/delete/update translations without a single line of JavaScript.
|
|
8
|
+
1. Uses a single field in the table. No additional tables are required to store translated data. PostgreSQL 9.4+ is required for this (JSONB).
|
|
9
|
+
2. Provides easy integration with forms using nested attributes, so you can create records with multiple translations in one form. Together with [nested_form_fields](https://github.com/ncri/nested_form_fields) you can dynamically add/delete/update translations without a single line of JavaScript.
|
|
11
10
|
|
|
12
11
|
```ruby
|
|
13
12
|
I18n.locale = :en
|
|
@@ -22,12 +21,12 @@ I18n.locale = :en
|
|
|
22
21
|
post.title #=> hello
|
|
23
22
|
```
|
|
24
23
|
|
|
25
|
-
It adds very thin abstraction layer on top of JSONB field. All data is stored in a simple JSON structure: `{ "locale_name": "data" }`. JSONB can be indexed (this is the main reason to
|
|
24
|
+
It adds a very thin abstraction layer on top of a JSONB field. All data is stored in a simple JSON structure: `{ "locale_name": "data" }`. JSONB can be indexed (this is the main reason to use it instead of plain JSON, which is available in earlier Postgres versions).
|
|
26
25
|
|
|
27
26
|
## Requirements
|
|
28
27
|
|
|
29
28
|
- PostgreSQL >= 9.4
|
|
30
|
-
- ActiveRecord >=
|
|
29
|
+
- ActiveRecord >= 5.0 (tested with 7.0–8.1)
|
|
31
30
|
- I18n
|
|
32
31
|
|
|
33
32
|
## Installation
|
|
@@ -48,9 +47,9 @@ Or install it yourself as:
|
|
|
48
47
|
|
|
49
48
|
## Usage
|
|
50
49
|
|
|
51
|
-
Include `Translateable` into your model (or `ApplicationRecord`
|
|
50
|
+
Include `Translateable` into your model (or into `ApplicationRecord` to make it available in all models).
|
|
52
51
|
|
|
53
|
-
Call `translateable` macro with a list of attributes you want to be translateable:
|
|
52
|
+
Call the `translateable` macro with a list of attributes you want to be translateable:
|
|
54
53
|
```ruby
|
|
55
54
|
class Post < ActiveRecord::Base
|
|
56
55
|
include Translateable
|
|
@@ -59,7 +58,7 @@ class Post < ActiveRecord::Base
|
|
|
59
58
|
end
|
|
60
59
|
```
|
|
61
60
|
|
|
62
|
-
Now `title` attribute is translateable:
|
|
61
|
+
Now the `title` attribute is translateable:
|
|
63
62
|
```ruby
|
|
64
63
|
I18n.locale = :en
|
|
65
64
|
post = Post.create(title: 'hello')
|
|
@@ -81,7 +80,7 @@ You can pass multiple attributes:
|
|
|
81
80
|
translateable :title, :body
|
|
82
81
|
```
|
|
83
82
|
|
|
84
|
-
If there is no translation for
|
|
83
|
+
If there is no translation for the selected locale, then `I18n.default_locale` will be used. If there is no translation for `I18n.default_locale`, then the first available one will be used. You can override this behavior with the `strict` option; in this case you'll get `nil` if there is no translation for the selected locale:
|
|
85
84
|
```ruby
|
|
86
85
|
I18n.locale = :en
|
|
87
86
|
post = Post.create(title: 'hello')
|
|
@@ -92,7 +91,7 @@ post.title #=> hello
|
|
|
92
91
|
post.title(strict: true) #=> nil
|
|
93
92
|
```
|
|
94
93
|
|
|
95
|
-
You can assign all locales
|
|
94
|
+
You can assign data for all locales at once as a hash:
|
|
96
95
|
```ruby
|
|
97
96
|
post = Post.create(title: { en: 'hello', ru: 'привет' })
|
|
98
97
|
I18n.with_locale(:en) do
|
|
@@ -103,13 +102,13 @@ I18n.with_locale(:ru) do
|
|
|
103
102
|
end
|
|
104
103
|
```
|
|
105
104
|
|
|
106
|
-
You can easily create translated data with form using nested attributes
|
|
105
|
+
You can easily create translated data with a form using nested attributes.
|
|
107
106
|
|
|
108
|
-
For example, with [simple_form](https://github.com/
|
|
107
|
+
For example, with [simple_form](https://github.com/heartcombo/simple_form) and [nested_form_fields](https://github.com/ncri/nested_form_fields):
|
|
109
108
|
```haml
|
|
110
109
|
= simple_form_for @post do |f|
|
|
111
110
|
= f.label(:title)
|
|
112
|
-
= f.nested_fields_for Translateable.translateable_attribute_by_name(:title), class_name: '
|
|
111
|
+
= f.nested_fields_for Translateable.translateable_attribute_by_name(:title), class_name: 'Translateable::AttributeValue' do |ff|
|
|
113
112
|
= ff.input :data, label: false
|
|
114
113
|
= ff.input :locale, collection: I18n.available_locales, include_blank: false, label: false
|
|
115
114
|
= ff.remove_nested_fields_link 'Remove translation', role: 'button'
|
|
@@ -117,11 +116,11 @@ For example, with [simple_form](https://github.com/plataformatec/simple_form) an
|
|
|
117
116
|
= f.button :submit
|
|
118
117
|
```
|
|
119
118
|
|
|
120
|
-
Or with built-in `form_for` and [nested_form_fields](https://github.com/ncri/nested_form_fields):
|
|
119
|
+
Or with the built-in `form_for` and [nested_form_fields](https://github.com/ncri/nested_form_fields):
|
|
121
120
|
```haml
|
|
122
121
|
= form_for @post do |f|
|
|
123
122
|
= f.label :title
|
|
124
|
-
= f.nested_fields_for Translateable.translateable_attribute_by_name(:title), class_name: '
|
|
123
|
+
= f.nested_fields_for Translateable.translateable_attribute_by_name(:title), class_name: 'Translateable::AttributeValue' do |ff|
|
|
125
124
|
= ff.text_field :data
|
|
126
125
|
= ff.select :locale, I18n.available_locales
|
|
127
126
|
= ff.remove_nested_fields_link 'Remove translation', role: 'button'
|
|
@@ -137,53 +136,53 @@ def post_params
|
|
|
137
136
|
end
|
|
138
137
|
|
|
139
138
|
# `translateable_permitted_attributes` method provides strong_params for all translateable attributes
|
|
140
|
-
# for example with `title` attribute those will be: `title_translateable_attributes: [:locale, :data, :_destroy]`
|
|
139
|
+
# for example, with the `title` attribute those will be: `title_translateable_attributes: [:locale, :data, :_destroy]`
|
|
141
140
|
```
|
|
142
|
-
Now you can add/delete/update `title` attribute value in different languages via a single form.
|
|
141
|
+
Now you can add/delete/update the `title` attribute value in different languages via a single form.
|
|
143
142
|
|
|
144
143
|
### Migration
|
|
145
144
|
|
|
146
|
-
Attributes must exist with `JSONB` type in
|
|
145
|
+
Attributes must exist with the `JSONB` type in the database, so create a migration:
|
|
147
146
|
```ruby
|
|
148
|
-
class AddTitleToPosts < ActiveRecord::Migration
|
|
147
|
+
class AddTitleToPosts < ActiveRecord::Migration[8.1]
|
|
149
148
|
def change
|
|
150
149
|
add_column :posts, :title, :jsonb, null: false, default: {}
|
|
151
150
|
end
|
|
152
151
|
end
|
|
153
152
|
```
|
|
154
153
|
|
|
155
|
-
If you already have data
|
|
154
|
+
If you already have data and want to migrate it to the new translateable structure, use the provided generator:
|
|
156
155
|
```
|
|
157
156
|
bin/rails generate translateable:migration posts title
|
|
158
157
|
```
|
|
159
|
-
This will create a reversible migration for data in `title` field of the `posts` table. By default, the
|
|
158
|
+
This will create a reversible migration for data in the `title` field of the `posts` table. By default, the existing data will be moved into `I18n.default_locale`. If you want to use another locale, provide it as a third argument:
|
|
160
159
|
```
|
|
161
160
|
bin/rails generate translateable:migration posts title ru
|
|
162
161
|
```
|
|
163
|
-
Now all
|
|
162
|
+
Now all existing data will be transferred into the new structure with the 'ru' locale.
|
|
164
163
|
|
|
165
|
-
Example (using 'en' locale):
|
|
164
|
+
Example (using the 'en' locale):
|
|
166
165
|
```sql
|
|
167
|
-
|
|
168
|
-
SELECT id,title FROM posts;
|
|
169
|
-
id |
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
id |
|
|
166
|
+
-- before migration
|
|
167
|
+
SELECT id, title FROM posts;
|
|
168
|
+
id | title
|
|
169
|
+
----+-------
|
|
170
|
+
1 | hello
|
|
171
|
+
2 | world
|
|
172
|
+
|
|
173
|
+
-- after migration
|
|
174
|
+
SELECT id, title FROM posts;
|
|
175
|
+
id | title
|
|
177
176
|
----+-----------------
|
|
178
177
|
1 | {"en": "hello"}
|
|
179
178
|
2 | {"en": "world"}
|
|
180
179
|
```
|
|
181
180
|
|
|
182
|
-
|
|
181
|
+
If the field was indexed, the migration re-creates the index as a GIN index. If you need a different index (e.g. on a JSON path), change the migration manually.
|
|
183
182
|
|
|
184
183
|
### Queries
|
|
185
184
|
|
|
186
|
-
You'll probably want to create scopes for
|
|
185
|
+
You'll probably want to create scopes for these kinds of queries.
|
|
187
186
|
|
|
188
187
|
```ruby
|
|
189
188
|
# get posts where `title` with `en` locale is 'hello'
|
|
@@ -193,39 +192,40 @@ Post.where("title->>'en' = ?", 'hello')
|
|
|
193
192
|
Post.where("EXISTS (SELECT 1 FROM jsonb_each_text(posts.title) j WHERE j.value = ?)", 'hola')
|
|
194
193
|
|
|
195
194
|
# get posts where `title` LIKE 'прив' with any locale ignoring case
|
|
196
|
-
where("EXISTS (SELECT 1 FROM jsonb_each_text(posts.title) j WHERE lower(j.value) LIKE ?)", '%прив%')
|
|
195
|
+
Post.where("EXISTS (SELECT 1 FROM jsonb_each_text(posts.title) j WHERE lower(j.value) LIKE ?)", '%прив%')
|
|
197
196
|
```
|
|
198
197
|
|
|
199
198
|
I use this concern:
|
|
200
199
|
```ruby
|
|
201
|
-
# app/models/concerns/
|
|
202
|
-
module
|
|
200
|
+
# app/models/concerns/jsonb_queryable.rb
|
|
201
|
+
module JsonbQueryable
|
|
203
202
|
extend ActiveSupport::Concern
|
|
204
203
|
|
|
205
204
|
included do
|
|
206
205
|
# http://stackoverflow.com/questions/36250331/query-postgres-jsonb-by-value-regardless-of-keys/36251296#36251296
|
|
207
|
-
scope :where_jsonb_value, ->
|
|
208
|
-
|
|
209
|
-
where("EXISTS (SELECT 1 FROM jsonb_each_text(#{
|
|
206
|
+
scope :where_jsonb_value, ->(attribute, value) {
|
|
207
|
+
column = connection.quote_table_name("#{table_name}.#{attribute}")
|
|
208
|
+
where("EXISTS (SELECT 1 FROM jsonb_each_text(#{column}) j WHERE j.value = ?)", value)
|
|
210
209
|
}
|
|
211
210
|
|
|
212
|
-
scope :where_jsonb_value_like, ->
|
|
213
|
-
|
|
211
|
+
scope :where_jsonb_value_like, ->(attribute, value, case_sens = false) {
|
|
212
|
+
column = connection.quote_table_name("#{table_name}.#{attribute}")
|
|
213
|
+
pattern = "%#{sanitize_sql_like(value)}%"
|
|
214
214
|
if case_sens
|
|
215
|
-
where("EXISTS (SELECT 1 FROM jsonb_each_text(#{
|
|
215
|
+
where("EXISTS (SELECT 1 FROM jsonb_each_text(#{column}) j WHERE j.value LIKE ?)", pattern)
|
|
216
216
|
else
|
|
217
|
-
where("EXISTS (SELECT 1 FROM jsonb_each_text(#{
|
|
217
|
+
where("EXISTS (SELECT 1 FROM jsonb_each_text(#{column}) j WHERE lower(j.value) LIKE lower(?))", pattern)
|
|
218
218
|
end
|
|
219
219
|
}
|
|
220
220
|
end
|
|
221
221
|
end
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
Refer to the [Postgres documentation](
|
|
224
|
+
Refer to the [Postgres documentation](https://www.postgresql.org/docs/current/functions-json.html).
|
|
225
225
|
|
|
226
226
|
### Troubleshooting
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
When a model is loaded, `translateable` checks that each attribute has a matching column. The check is skipped if there is no active database connection or the table doesn't exist yet. If it still causes problems (e.g. a database that doesn't exist upon loading), you can disable it by setting the `DISABLE_TRANSLATEABLE_SANITY_CHECK` environment variable (any value) or by passing `sanity_checks: false`, for example `translateable :title, sanity_checks: false`.
|
|
229
229
|
|
|
230
230
|
## TODO
|
|
231
231
|
|
|
@@ -236,12 +236,14 @@ If you're having problems with `translateable_sanity_check` due to non-existing
|
|
|
236
236
|
|
|
237
237
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
238
238
|
|
|
239
|
+
Tests need a running PostgreSQL server, configured via `POSTGRES_HOST`, `POSTGRES_PORT` and `POSTGRES_PASSWORD`. To test against a specific Rails version, use one of the gemfiles, e.g. `BUNDLE_GEMFILE=gemfiles/8.1.gemfile bundle exec rspec`.
|
|
240
|
+
|
|
239
241
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
240
242
|
|
|
241
243
|
## Contributing
|
|
242
244
|
|
|
243
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/olegantonyan/translateable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](
|
|
245
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/olegantonyan/translateable. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](https://www.contributor-covenant.org) code of conduct.
|
|
244
246
|
|
|
245
247
|
## License
|
|
246
248
|
|
|
247
|
-
The gem is available as open source under the terms of the [MIT License](
|
|
249
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/gemfiles/7.0.gemfile
CHANGED
|
@@ -2,6 +2,7 @@ source 'https://rubygems.org'
|
|
|
2
2
|
|
|
3
3
|
gem 'activerecord', '~> 7.0.0'
|
|
4
4
|
gem 'activesupport', '~> 7.0.0'
|
|
5
|
+
gem 'railties', '~> 7.0.0'
|
|
5
6
|
gem 'concurrent-ruby', '1.3.4' # https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror
|
|
6
7
|
|
|
7
8
|
gemspec path: '../'
|
data/gemfiles/7.1.gemfile
CHANGED
data/gemfiles/7.2.gemfile
CHANGED
data/gemfiles/8.0.gemfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version %>
|
|
2
2
|
def change
|
|
3
3
|
reversible do |dir|
|
|
4
4
|
dir.up do
|
|
@@ -7,7 +7,7 @@ class MigrateTranslateable<%= table_name.capitalize + field_name.capitalize %> <
|
|
|
7
7
|
UPDATE <%= table_name %> AS m1
|
|
8
8
|
SET <%= field_name + '_t' %> = (
|
|
9
9
|
SELECT row_to_json(t) FROM (
|
|
10
|
-
SELECT <%= field_name %> AS <%= locale %> FROM <%= table_name %> AS m2 WHERE m1.id = m2.id
|
|
10
|
+
SELECT <%= field_name %> AS "<%= locale %>" FROM <%= table_name %> AS m2 WHERE m1.id = m2.id
|
|
11
11
|
) t
|
|
12
12
|
)::jsonb;
|
|
13
13
|
SQL
|
|
@@ -23,7 +23,7 @@ class MigrateTranslateable<%= table_name.capitalize + field_name.capitalize %> <
|
|
|
23
23
|
execute <<-SQL
|
|
24
24
|
UPDATE <%= table_name %> AS m1
|
|
25
25
|
SET <%= field_name + '_t' %> = (
|
|
26
|
-
SELECT <%= field_name %>->>'<%= locale %>'
|
|
26
|
+
SELECT COALESCE(<%= field_name %>->>'<%= locale %>', '') FROM <%= table_name %> AS m2 WHERE m1.id = m2.id
|
|
27
27
|
);
|
|
28
28
|
SQL
|
|
29
29
|
remove_column :<%= table_name %>, :<%= field_name %>
|
data/lib/translateable.rb
CHANGED
|
@@ -9,7 +9,13 @@ module Translateable
|
|
|
9
9
|
"#{attr}_translateable"
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
AttributeValue = Struct.new(:locale, :data)
|
|
12
|
+
AttributeValue = Struct.new(:locale, :data, keyword_init: true) do
|
|
13
|
+
def _destroy; end
|
|
14
|
+
|
|
15
|
+
def persisted?
|
|
16
|
+
false
|
|
17
|
+
end
|
|
18
|
+
end
|
|
13
19
|
|
|
14
20
|
module ClassMethods
|
|
15
21
|
def translateable(*attrs, sanity_checks: true)
|
|
@@ -17,45 +23,48 @@ module Translateable
|
|
|
17
23
|
translateable_sanity_check(attr) if sanity_checks
|
|
18
24
|
define_translateable_methods(attr)
|
|
19
25
|
end
|
|
20
|
-
|
|
26
|
+
@translateable_attributes = (@translateable_attributes || []) | attrs
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def translateable_attributes
|
|
30
|
+
inherited = superclass.respond_to?(:translateable_attributes) ? superclass.translateable_attributes : []
|
|
31
|
+
inherited | (@translateable_attributes || [])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def translateable_permitted_attributes
|
|
35
|
+
translateable_attributes.map { |attr| { "#{attr}_translateable_attributes" => %i(locale data _destroy) } }
|
|
21
36
|
end
|
|
22
37
|
|
|
23
38
|
def translateable_sanity_check(attr)
|
|
24
39
|
return if ENV['DISABLE_TRANSLATEABLE_SANITY_CHECK']
|
|
25
|
-
return unless database_connection_exists?
|
|
40
|
+
return unless database_connection_exists? && table_exists?
|
|
26
41
|
attr = attr.to_s
|
|
27
42
|
raise ArgumentError, "no such column '#{attr}' in '#{name}' model" unless column_names.include?(attr)
|
|
28
43
|
end
|
|
29
44
|
|
|
30
|
-
def define_translateable_strong_params(*attrs)
|
|
31
|
-
define_singleton_method('translateable_permitted_attributes') do
|
|
32
|
-
attrs.each_with_object([]) { |i, obj| obj << { "#{i}_translateable_attributes" => %i(locale data _destroy) } }
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
45
|
def database_connection_exists?
|
|
37
|
-
|
|
46
|
+
connection_pool.with_connection(&:active?)
|
|
38
47
|
rescue StandardError
|
|
39
48
|
false
|
|
40
49
|
end
|
|
41
50
|
|
|
42
51
|
def define_translateable_methods(attr)
|
|
43
52
|
define_method("#{attr}_fetch_translateable") do
|
|
44
|
-
|
|
45
|
-
return value.with_indifferent_access if !value.nil? && !value.empty?
|
|
46
|
-
(new_record? ? { I18n.locale => '' } : {}).with_indifferent_access
|
|
53
|
+
(self[attr] || {}).with_indifferent_access
|
|
47
54
|
end
|
|
48
55
|
|
|
49
56
|
define_method(Translateable.translateable_attribute_by_name(attr)) do
|
|
50
57
|
value = send("#{attr}_fetch_translateable")
|
|
58
|
+
value = { I18n.locale.to_s => '' } if value.empty? && new_record?
|
|
51
59
|
value.map { |k, v| AttributeValue.new(locale: k, data: v) }
|
|
52
60
|
end
|
|
53
61
|
|
|
54
62
|
define_method("#{attr}_translateable_attributes=") do |arg|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
entries = arg.respond_to?(:values) ? arg.values : arg
|
|
64
|
+
self[attr] = entries.each_with_object({}) do |entry, obj|
|
|
65
|
+
entry = entry.with_indifferent_access if entry.is_a?(Hash)
|
|
66
|
+
next if ActiveModel::Type::Boolean.new.cast(entry[:_destroy])
|
|
67
|
+
obj[entry[:locale].to_s] = entry[:data]
|
|
59
68
|
end
|
|
60
69
|
end
|
|
61
70
|
|
|
@@ -65,8 +74,7 @@ module Translateable
|
|
|
65
74
|
end
|
|
66
75
|
|
|
67
76
|
define_method("#{attr}=") do |arg|
|
|
68
|
-
|
|
69
|
-
self[attr] = value
|
|
77
|
+
self[attr] = arg.respond_to?(:to_hash) ? arg.to_hash : (self[attr] || {}).merge(I18n.locale.to_s => arg)
|
|
70
78
|
end
|
|
71
79
|
end
|
|
72
80
|
end
|
data/translateable.gemspec
CHANGED
|
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.authors = ['Oleg Antonyan']
|
|
9
9
|
spec.email = ['oleg.b.antonyan@gmail.com']
|
|
10
10
|
|
|
11
|
-
spec.summary = '
|
|
12
|
-
spec.description = "Similar to globalize, but uses PostgreSQL's JSONB to store data in a single field. No additional tables required.
|
|
11
|
+
spec.summary = 'Stores text data in multiple languages.'
|
|
12
|
+
spec.description = "Similar to globalize, but uses PostgreSQL's JSONB to store data in a single field. No additional tables required. A very thin abstraction."
|
|
13
13
|
spec.homepage = 'https://github.com/olegantonyan/translateable'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
15
|
|
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.require_paths = %w(lib)
|
|
20
20
|
|
|
21
21
|
spec.add_development_dependency 'bundler'
|
|
22
|
+
spec.add_development_dependency 'railties'
|
|
22
23
|
spec.add_development_dependency 'rake'
|
|
23
24
|
spec.add_development_dependency 'rspec'
|
|
24
25
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: translateable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oleg Antonyan
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: bundler
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: railties
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: rake
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,7 +122,7 @@ dependencies:
|
|
|
108
122
|
- !ruby/object:Gem::Version
|
|
109
123
|
version: '0'
|
|
110
124
|
description: Similar to globalize, but uses PostgreSQL's JSONB to store data in a
|
|
111
|
-
single field. No additional tables required.
|
|
125
|
+
single field. No additional tables required. A very thin abstraction.
|
|
112
126
|
email:
|
|
113
127
|
- oleg.b.antonyan@gmail.com
|
|
114
128
|
executables: []
|
|
@@ -129,6 +143,7 @@ files:
|
|
|
129
143
|
- gemfiles/7.1.gemfile
|
|
130
144
|
- gemfiles/7.2.gemfile
|
|
131
145
|
- gemfiles/8.0.gemfile
|
|
146
|
+
- gemfiles/8.1.gemfile
|
|
132
147
|
- lib/generators/translateable/migration_generator.rb
|
|
133
148
|
- lib/generators/translateable/templates/migration.rb.erb
|
|
134
149
|
- lib/translateable.rb
|
|
@@ -152,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
152
167
|
- !ruby/object:Gem::Version
|
|
153
168
|
version: '0'
|
|
154
169
|
requirements: []
|
|
155
|
-
rubygems_version:
|
|
170
|
+
rubygems_version: 4.0.16
|
|
156
171
|
specification_version: 4
|
|
157
|
-
summary:
|
|
172
|
+
summary: Stores text data in multiple languages.
|
|
158
173
|
test_files: []
|