from_clause_translate 0.2.6 → 0.2.7
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/README.md +149 -5
- data/lib/from_clause_translate.rb +3 -8
- data/lib/from_clause_translate/active_record_relation.rb +12 -10
- data/lib/from_clause_translate/class_methods.rb +11 -90
- data/lib/from_clause_translate/fallback.rb +53 -0
- data/lib/from_clause_translate/translated_columns_builder.rb +13 -8
- data/lib/from_clause_translate/translation_data.rb +89 -0
- data/lib/from_clause_translate/version.rb +1 -1
- metadata +65 -9
- data/lib/from_clause_translate/class_methods/fallbacks.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2a0a04072d1c8cb05f90443fa05aa74ffceaced4a714761abe83940b36a694a
|
4
|
+
data.tar.gz: 9c01576a7a7dcb5124bf82a0b8d88c61d67126bcbf41dc9dabec1d3c2878f2c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ad0b659cd25294a584f9bf293f74fbda4006b1f71bea76ba0cdd0abc293a6177dd027dc204a13173d8a4ca361b34c665f392e49fa59617828142f6731c6114c
|
7
|
+
data.tar.gz: 8437b273f89e0780289287f26cef363a56ffab850c2133226a49a73ca2c45597f38aaf8e982cbb175988d6f4872b9f8291e0d4442d4c0c3cb00203770a46ad81
|
data/README.md
CHANGED
@@ -1,8 +1,155 @@
|
|
1
1
|
# FromClauseTranslate
|
2
|
-
|
2
|
+
This gem extends ActiveRecord models for easy manipulations with multilanguage data.
|
3
|
+
|
4
|
+
The name of gem stands for way that it is doing its magic - SQL's `FROM` statement.
|
5
|
+
|
6
|
+
The main idea is that you can have database columns `name_en`, `name_ru`, `name_fr` and you will still able to filter data with `WHERE name LIKE '%some name%'`, all `SQL` statements will be working with just `name` column.
|
7
|
+
|
8
|
+
It allows making of complex `SQL` queries without need of interpolation of `I18n.locale` and without joining separate translation tables.
|
9
|
+
|
10
|
+
## Requirements
|
11
|
+
|
12
|
+
Activerecord.
|
13
|
+
|
14
|
+
Postgresql, other databases were not tested.
|
3
15
|
|
4
16
|
## Usage
|
5
|
-
|
17
|
+
Lets have translatable model `Post`:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class CreatePosts < ActiveRecord::Migration[5.2]
|
21
|
+
def change
|
22
|
+
create_table :posts do |t|
|
23
|
+
%i[en ru uk de fr it es].each do |locale|
|
24
|
+
t.string "slug_#{locale}"
|
25
|
+
t.string "name_#{locale}"
|
26
|
+
t.string "title_#{locale}"
|
27
|
+
t.string "text_#{locale}"
|
28
|
+
t.boolean "translated_#{locale}", null: false, default: false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
In migration array of locales should be hardcoded, because when new language will be added in future, this migration will still be reversable.
|
36
|
+
|
37
|
+
Then this `FromClauseTranslate` should be included in `AplicationRecord` or in the required model:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class ApplicationRecord < ActiveRecord::Base
|
41
|
+
include FromClauseTranslate
|
42
|
+
|
43
|
+
self.abstract_class = true
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
Make columns in the model translatable:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
class Post < ApplicationRecord
|
51
|
+
translates :name, :title, :text, :slug, :translated, plurals: %i[slugs translateds]
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
About plurals will be down below, now this model supports getting and setting values:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
post = Post.new(name: 'Name for current locale')
|
59
|
+
post.name == 'Name for current locale' # true
|
60
|
+
post.name = 'New name'
|
61
|
+
post.name_fr = 'French name of post' # real columns are still accessible
|
62
|
+
```
|
63
|
+
|
64
|
+
Such ActiveRecord column methods are supported (star for column name):
|
65
|
+
`save_change_to_*`, `*_changed?`, `*_before_last_save`, `*_change_to_be_saved`, `*_in_database`, `saved_change_to_*?`, `will_save_change_to_*?`
|
66
|
+
|
67
|
+
## Querying
|
68
|
+
|
69
|
+
Following code will get posts just like they are in DB, with all of `name_en`, `title_de` columns:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
post = Post.take
|
73
|
+
posts = Post.all
|
74
|
+
```
|
75
|
+
|
76
|
+
For receiving columns for current locale use `.translated` method with the list of required columns:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
post = Post.translated(:title, :name, :text)
|
80
|
+
```
|
81
|
+
|
82
|
+
It accepts symbols and `SQL` strings that will go to `SELECT` statement.
|
83
|
+
|
84
|
+
After invoking `.translated` passed columns becomes available in querying methods:
|
85
|
+
```ruby
|
86
|
+
Post.translated(:title, :name)
|
87
|
+
.where(title: 'smth')
|
88
|
+
.where("name ILIKE '%name%'")
|
89
|
+
.order('title DESC, name ASC')
|
90
|
+
```
|
91
|
+
|
92
|
+
## Fallbacks
|
93
|
+
|
94
|
+
Set up `I18n.fallbacks` in `application.rb`:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
I18n.fallbacks = {uk: [:ru, :en]}
|
98
|
+
```
|
99
|
+
|
100
|
+
Now ukrainian will fallback to russian and then to english.
|
101
|
+
|
102
|
+
Lets take post record:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
Post.translated(:name).take
|
106
|
+
```
|
107
|
+
|
108
|
+
Fallback rule will be present in produced `SQL` statement:
|
109
|
+
|
110
|
+
```sql
|
111
|
+
COALESCE(COALESCE("posts"."name_uk", name_ru), name_en) AS name
|
112
|
+
```
|
113
|
+
|
114
|
+
Fallbacks could be set through option of `.translated` method in model:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
class Post
|
118
|
+
translates :name, fallback: {_: %i[name_en title_en]}
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
Here `_` key means any locale.
|
123
|
+
If current locale is `ru` and `name_ru` is `NULL` then `name_en` will be used.
|
124
|
+
If it is `NULL` too then `title_en` will be received.
|
125
|
+
If current locale is `en` then `name_en` fallback rule will be ignored and `title_en` will be received.
|
126
|
+
|
127
|
+
## Plurals
|
128
|
+
|
129
|
+
Described Post model has `slug` columns for human readable urls.
|
130
|
+
Also it has `translated` boolean columns for hiding not translated posts from users.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class Post < ApplicationRecord
|
134
|
+
translates :name, ..., plurals: %i[slugs translateds]
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
Query for show page loads `slug` and `translated` columns for all locales:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
post = Post.translated(:slug, :slugs, :translateds).find_by(slug: params[:slug])
|
142
|
+
```
|
143
|
+
|
144
|
+
Now at the show page you can provide links to same post in different languages filtering out not translated:
|
145
|
+
|
146
|
+
```erbruby
|
147
|
+
<% I18n.available_locales.each do |locale| %>
|
148
|
+
<% if @post.send("translated_#{locale}") %>
|
149
|
+
<% link_to "Read in #{locale}", post_url(@post.send("slug_#{locale}")) %>
|
150
|
+
<% end %>
|
151
|
+
<% end %>
|
152
|
+
```
|
6
153
|
|
7
154
|
## Installation
|
8
155
|
Add this line to your application's Gemfile:
|
@@ -21,8 +168,5 @@ Or install it yourself as:
|
|
21
168
|
$ gem install from_clause_translate
|
22
169
|
```
|
23
170
|
|
24
|
-
## Contributing
|
25
|
-
Contribution directions go here.
|
26
|
-
|
27
171
|
## License
|
28
172
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
@@ -2,14 +2,9 @@ module FromClauseTranslate
|
|
2
2
|
require 'active_record'
|
3
3
|
require 'from_clause_translate/active_record_relation'
|
4
4
|
require 'from_clause_translate/class_methods'
|
5
|
+
require 'from_clause_translate/translation_data'
|
5
6
|
|
6
|
-
def self.included
|
7
|
-
model.
|
8
|
-
model.extend ClassMethods
|
9
|
-
model.const_set :TRANSLATED, {}
|
10
|
-
model.const_set :TRANSLATED_PLURALS, {}
|
11
|
-
model.const_set :TRANSLATED_SELECTION, Hash[
|
12
|
-
I18n.available_locales.map { |locale| [locale, {}] }
|
13
|
-
]
|
7
|
+
def self.included(model)
|
8
|
+
model.extend FromClauseTranslate::ClassMethods
|
14
9
|
end
|
15
10
|
end
|
@@ -1,33 +1,35 @@
|
|
1
1
|
require 'from_clause_translate/translated_columns_builder'
|
2
2
|
|
3
3
|
module ActiveRecord::Relation::Extended
|
4
|
-
def arel
|
4
|
+
def arel(aliases = nil)
|
5
|
+
arel = super
|
5
6
|
apply_translates
|
6
|
-
|
7
|
+
arel.instance_variable_get(:@ctx).source.left = @from_clause if @from_clause
|
8
|
+
arel
|
7
9
|
end
|
8
10
|
|
9
|
-
def add_translated_columns
|
11
|
+
def add_translated_columns(columns)
|
10
12
|
(@values[:translated] ||= []).concat columns
|
11
13
|
end
|
12
14
|
|
13
|
-
def add_translated_column
|
15
|
+
def add_translated_column(column)
|
14
16
|
(@values[:translated] ||= []) << column
|
15
17
|
end
|
16
18
|
|
17
19
|
def apply_translates
|
18
|
-
return if @translates_applied || !@
|
20
|
+
return if @translates_applied || !@values[:translated]
|
19
21
|
|
20
22
|
@translates_applied = true
|
21
23
|
|
22
24
|
columns = translated_columns_build
|
23
25
|
return unless columns
|
24
26
|
|
25
|
-
@arel = build_arel nil
|
26
|
-
|
27
27
|
rel = unscoped.select(columns)
|
28
28
|
rel.instance_variable_set(:@translates_applied, true)
|
29
29
|
replace_plurals_in_projections
|
30
|
-
@
|
30
|
+
@from_clause = Arel::Nodes::SqlLiteral.new(
|
31
|
+
"(#{rel.arel.to_sql}) #{@klass.table_name}"
|
32
|
+
)
|
31
33
|
end
|
32
34
|
|
33
35
|
def translated_columns_build
|
@@ -36,7 +38,7 @@ module ActiveRecord::Relation::Extended
|
|
36
38
|
).perform
|
37
39
|
end
|
38
40
|
|
39
|
-
def perform_calculation
|
41
|
+
def perform_calculation(operation, column_name)
|
40
42
|
return super operation, column_name if @without_projections
|
41
43
|
|
42
44
|
rel = spawn
|
@@ -50,7 +52,7 @@ module ActiveRecord::Relation::Extended
|
|
50
52
|
next unless column.is_a? String
|
51
53
|
|
52
54
|
name = column[0] == '"' ? column[1...-1] : column
|
53
|
-
plural = @klass
|
55
|
+
plural = @klass._translation_data.plurals[name.to_sym]
|
54
56
|
column.replace plural if plural
|
55
57
|
end
|
56
58
|
end
|
@@ -1,106 +1,27 @@
|
|
1
|
-
module ClassMethods
|
2
|
-
|
3
|
-
include Fallbacks
|
4
|
-
|
5
|
-
def translates *columns
|
1
|
+
module FromClauseTranslate::ClassMethods
|
2
|
+
def translates(*columns)
|
6
3
|
options = columns.extract_options!
|
7
|
-
|
8
|
-
map_translated_columns(columns)
|
9
|
-
).each do |hash|
|
10
|
-
column = hash[:name]
|
11
|
-
self::TRANSLATED[column] = true
|
12
|
-
define_translated_selection column, hash
|
13
|
-
define_translated_column_methods column
|
14
|
-
end
|
4
|
+
_translation_data.add_columns(columns)
|
15
5
|
translates_plurals options[:plurals]
|
16
6
|
end
|
17
7
|
|
18
|
-
def translates_plurals
|
19
|
-
|
20
|
-
|
21
|
-
plurals.each do |plural|
|
22
|
-
plural = plural.to_sym
|
23
|
-
self::TRANSLATED[plural] = true
|
24
|
-
column = plural.to_s.singularize.to_sym
|
25
|
-
selection = I18n.available_locales.map do |locale|
|
26
|
-
"#{quoted_table_name}.\"#{column}_#{locale}\""
|
27
|
-
end.join ','
|
28
|
-
self::TRANSLATED_PLURALS[plural] = selection
|
29
|
-
I18n.available_locales.each do |locale|
|
30
|
-
self::TRANSLATED_SELECTION[locale][plural] = selection
|
31
|
-
end
|
8
|
+
def translates_plurals(plurals)
|
9
|
+
plurals && plurals.each do |plural|
|
10
|
+
_translation_data.add_plural(plural)
|
32
11
|
end
|
33
12
|
end
|
34
13
|
|
35
|
-
def translates?
|
36
|
-
|
14
|
+
def translates?(column)
|
15
|
+
_translation_data.translates? column
|
37
16
|
end
|
38
17
|
|
39
|
-
def translated
|
18
|
+
def translated(*columns)
|
40
19
|
scope = current_scope || all
|
41
20
|
scope.add_translated_columns columns
|
42
21
|
scope
|
43
22
|
end
|
44
23
|
|
45
|
-
|
46
|
-
|
47
|
-
def map_translated_columns columns
|
48
|
-
columns.map! do |column|
|
49
|
-
hash = column.is_a?(Hash) ? column : {name: column}
|
50
|
-
translated_fallbacks_set hash
|
51
|
-
hash
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def filter_translated_columns columns
|
56
|
-
columns.keep_if do |hash|
|
57
|
-
!translates?(hash[:name]) &&
|
58
|
-
column_names.include?("#{hash[:name]}_#{I18n.locale}")
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def define_translated_selection column, hash
|
63
|
-
fallback = hash[:fallback]
|
64
|
-
I18n.available_locales.each do |locale|
|
65
|
-
selection = "#{quoted_table_name}.\"#{column}_#{locale}\""
|
66
|
-
selection = translated_fallbacks_wrap selection, fallback[locale]
|
67
|
-
selection = translated_fallbacks_wrap selection, fallback[:_]
|
68
|
-
self::TRANSLATED_SELECTION[locale][column] = "#{selection} AS #{column}"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def define_translated_column_methods col
|
73
|
-
col = col.to_s
|
74
|
-
dashed = "#{col}_"
|
75
|
-
define_translated_column_getter_and_setter col, dashed
|
76
|
-
define_translated_column_changing_methods col, dashed
|
77
|
-
end
|
78
|
-
|
79
|
-
def define_translated_column_getter_and_setter col, dashed
|
80
|
-
define_method col do
|
81
|
-
@attributes.key?(col) ? self[col] : send("#{dashed}#{I18n.locale}")
|
82
|
-
end
|
83
|
-
|
84
|
-
define_method col + '=' do |val|
|
85
|
-
send "#{dashed}#{I18n.locale}=", val
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def define_translated_column_changing_methods col, dashed
|
90
|
-
define_method 'saved_change_to_' + col do
|
91
|
-
send "saved_change_to_#{dashed}#{I18n.locale}"
|
92
|
-
end
|
93
|
-
|
94
|
-
%w[changed? before_last_save change_to_be_saved in_database].each do |suf|
|
95
|
-
define_method dashed + suf do
|
96
|
-
send "#{dashed}#{I18n.locale}_#{suf}"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
%w[saved_change_to will_save_change_to].each do |prefix|
|
101
|
-
define_method "#{prefix}_#{col}?" do
|
102
|
-
send "#{prefix}_#{dashed}#{I18n.locale}?"
|
103
|
-
end
|
104
|
-
end
|
24
|
+
def _translation_data
|
25
|
+
@translation_data ||= FromClauseTranslate::TranslationData.new(self)
|
105
26
|
end
|
106
27
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class FromClauseTranslate::Fallback
|
2
|
+
attr_reader :model, :fallback
|
3
|
+
|
4
|
+
def initialize(model, hash)
|
5
|
+
@model = model
|
6
|
+
@fallback = hash[:fallback]
|
7
|
+
|
8
|
+
if fallback.nil?
|
9
|
+
create_fallback_from_i18n(hash[:name])
|
10
|
+
elsif !fallback.is_a? Hash
|
11
|
+
@fallback = {_: fallback}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def wrap(selection, column, locale)
|
16
|
+
wrap_with_fallbacks selection, fallback[locale], column, locale
|
17
|
+
wrap_with_fallbacks selection, fallback[:_], column, locale
|
18
|
+
selection
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def wrap_with_fallbacks(selection, fallbacks, column, locale)
|
24
|
+
return unless fallbacks
|
25
|
+
|
26
|
+
if fallbacks.is_a? Array
|
27
|
+
fallbacks.each do |fallback|
|
28
|
+
wrap_with_fallbacks selection, fallback, column, locale
|
29
|
+
end
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
fallbacks = "#{model.table_name}.#{fallbacks}" if fallbacks.is_a? Symbol
|
34
|
+
return if fallbacks == "#{model.table_name}.#{column}_#{locale}"
|
35
|
+
|
36
|
+
selection.replace "COALESCE(#{selection}, #{fallbacks})"
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_fallback_from_i18n(column)
|
40
|
+
if I18n.fallbacks.empty?
|
41
|
+
# invoke fallbacks generation
|
42
|
+
I18n.available_locales.each { |locale| I18n.fallbacks[locale] }
|
43
|
+
end
|
44
|
+
@fallback = {}
|
45
|
+
I18n.fallbacks.each do |locale, array|
|
46
|
+
result = []
|
47
|
+
fallback[locale] = result
|
48
|
+
array.each do |fallback|
|
49
|
+
result << "#{column}_#{fallback}" unless fallback == locale
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -79,9 +79,12 @@ class FromClauseTranslate::TranslatedColumnsBuilder
|
|
79
79
|
next unless child.respond_to? :left
|
80
80
|
|
81
81
|
left = child.left
|
82
|
-
|
82
|
+
if left.respond_to?(:relation) &&
|
83
|
+
left.relation.table_name != @klass.table_name
|
84
|
+
next
|
85
|
+
end
|
83
86
|
|
84
|
-
translated_selection left.name
|
87
|
+
translated_selection child.left.name
|
85
88
|
end
|
86
89
|
end
|
87
90
|
end
|
@@ -100,22 +103,24 @@ class FromClauseTranslate::TranslatedColumnsBuilder
|
|
100
103
|
|
101
104
|
def add_translated_columns_from_orders
|
102
105
|
@arel.ast.orders.each do |name|
|
103
|
-
|
106
|
+
if name.respond_to?(:expr)
|
107
|
+
name = name.expr
|
108
|
+
name = name.name if name.respond_to?(:name)
|
109
|
+
end
|
104
110
|
translated_selection name
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
108
114
|
def translated_selection name, original = nil
|
109
115
|
sym = name.to_sym
|
110
|
-
translates = @klass.translates? sym
|
116
|
+
translates = @klass.respond_to?(:translates?) && @klass.translates?(sym)
|
111
117
|
@has_translated = true
|
112
118
|
if translates
|
113
|
-
return @columns << @klass
|
119
|
+
return @columns << @klass._translation_data.selections[I18n.locale][sym]
|
114
120
|
end
|
115
121
|
|
116
|
-
|
117
|
-
return if @select_all || !@klass.columns_hash[name.to_s]
|
122
|
+
return unless @klass.columns_hash[name.to_s]
|
118
123
|
|
119
|
-
@columns << (original
|
124
|
+
@columns << (original ? original.to_sym : sym)
|
120
125
|
end
|
121
126
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'from_clause_translate/fallback'
|
2
|
+
|
3
|
+
class FromClauseTranslate::TranslationData
|
4
|
+
attr_reader :model, :translated, :plurals, :selections
|
5
|
+
|
6
|
+
def initialize(model)
|
7
|
+
@model = model
|
8
|
+
@translated = {}
|
9
|
+
@plurals = {}
|
10
|
+
@selections = I18n.available_locales.map { |locale| [locale, {}] }.to_h
|
11
|
+
end
|
12
|
+
|
13
|
+
def translates?(column)
|
14
|
+
@translated[column.to_sym] || false
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_columns(columns)
|
18
|
+
columns.each(&method(:add_column))
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_column(column)
|
22
|
+
hash = column.is_a?(Hash) ? column : {name: column}
|
23
|
+
|
24
|
+
return if translates?(hash[:name]) ||
|
25
|
+
!model.column_names.include?("#{hash[:name]}_#{I18n.locale}")
|
26
|
+
|
27
|
+
column = hash[:name]
|
28
|
+
translated[column] = true
|
29
|
+
|
30
|
+
define_translated_selection column, hash
|
31
|
+
|
32
|
+
column = column.to_s
|
33
|
+
dashed = "#{column}_"
|
34
|
+
define_translated_column_getter_and_setter column, dashed
|
35
|
+
define_translated_column_changing_methods column, dashed
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_plural(plural)
|
39
|
+
plural = plural.to_sym
|
40
|
+
translated[plural] = true
|
41
|
+
column = plural.to_s.singularize.to_sym
|
42
|
+
selection = I18n.available_locales.map do |locale|
|
43
|
+
"#{model.quoted_table_name}.\"#{column}_#{locale}\""
|
44
|
+
end.join ','
|
45
|
+
plurals[plural] = selection
|
46
|
+
I18n.available_locales.each do |locale|
|
47
|
+
selections[locale][plural] = selection
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def define_translated_selection(column, hash)
|
54
|
+
fallback = FromClauseTranslate::Fallback.new(model, hash)
|
55
|
+
I18n.available_locales.each do |locale|
|
56
|
+
selection = "#{model.quoted_table_name}.\"#{column}_#{locale}\""
|
57
|
+
selection = fallback.wrap selection, column, locale
|
58
|
+
selections[locale][column] = "#{selection} AS #{column}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_translated_column_getter_and_setter col, dashed
|
63
|
+
model.define_method col do
|
64
|
+
@attributes.key?(col) ? self[col] : send("#{dashed}#{I18n.locale}")
|
65
|
+
end
|
66
|
+
|
67
|
+
model.define_method col + '=' do |val|
|
68
|
+
send "#{dashed}#{I18n.locale}=", val
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def define_translated_column_changing_methods col, dashed
|
73
|
+
model.define_method 'saved_change_to_' + col do
|
74
|
+
send "saved_change_to_#{dashed}#{I18n.locale}"
|
75
|
+
end
|
76
|
+
|
77
|
+
%w[changed? before_last_save change_to_be_saved in_database].each do |suf|
|
78
|
+
model.define_method dashed + suf do
|
79
|
+
send "#{dashed}#{I18n.locale}_#{suf}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
%w[saved_change_to will_save_change_to].each do |prefix|
|
84
|
+
model.define_method "#{prefix}_#{col}?" do
|
85
|
+
send "#{prefix}_#{dashed}#{I18n.locale}?"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: from_clause_translate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Kushin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '5'
|
19
|
+
version: '5.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '5'
|
26
|
+
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: standalone_migrations
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: database_cleaner
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
27
83
|
description: Translates record using SQL from clause.
|
28
84
|
email:
|
29
85
|
- romadzao@gmail.com
|
@@ -36,10 +92,11 @@ files:
|
|
36
92
|
- lib/from_clause_translate.rb
|
37
93
|
- lib/from_clause_translate/active_record_relation.rb
|
38
94
|
- lib/from_clause_translate/class_methods.rb
|
39
|
-
- lib/from_clause_translate/
|
95
|
+
- lib/from_clause_translate/fallback.rb
|
40
96
|
- lib/from_clause_translate/translated_columns_builder.rb
|
97
|
+
- lib/from_clause_translate/translation_data.rb
|
41
98
|
- lib/from_clause_translate/version.rb
|
42
|
-
homepage:
|
99
|
+
homepage: https://gitlab.com/romikus/rails-from-clause-translate-gem
|
43
100
|
licenses:
|
44
101
|
- MIT
|
45
102
|
metadata: {}
|
@@ -58,8 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
115
|
- !ruby/object:Gem::Version
|
59
116
|
version: '0'
|
60
117
|
requirements: []
|
61
|
-
|
62
|
-
rubygems_version: 2.7.6
|
118
|
+
rubygems_version: 3.0.3
|
63
119
|
signing_key:
|
64
120
|
specification_version: 4
|
65
121
|
summary: For database record translations
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module Fallbacks
|
2
|
-
private
|
3
|
-
|
4
|
-
def translated_fallbacks_set hash
|
5
|
-
fallback = hash[:fallback]
|
6
|
-
return hash[:fallback] = {} unless hash[:fallback]
|
7
|
-
return if fallback.is_a? Hash
|
8
|
-
|
9
|
-
hash[:fallback] = {_: fallback}
|
10
|
-
end
|
11
|
-
|
12
|
-
def translated_fallbacks_wrap selection, fallbacks
|
13
|
-
return selection unless fallbacks
|
14
|
-
|
15
|
-
if fallbacks.is_a?(String) || fallbacks.is_a?(Symbol)
|
16
|
-
translated_selection_fallback_wrap selection, fallbacks
|
17
|
-
else
|
18
|
-
fallbacks.each do |fallback|
|
19
|
-
selection = translated_selection_fallback_wrap selection, fallback
|
20
|
-
end
|
21
|
-
selection
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def translated_selection_fallback_wrap selection, fallback
|
26
|
-
if fallback.is_a? String
|
27
|
-
"COALESCE(#{selection}, #{fallback})"
|
28
|
-
else
|
29
|
-
"COALESCE(#{selection}, #{table_name}.#{fallback})"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|