Skip to content

Commit a4df123

Browse files
committed
Merge branch 'releases/2.0.0'
2 parents c737bcf + 3744838 commit a4df123

16 files changed

+628
-507
lines changed

.travis.yml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
11
language: elixir
22
elixir:
3-
- 1.2.5
43
- 1.3.2
54
- 1.3.3
65
- 1.3.4
76
- 1.4.0
87
- 1.4.1
8+
- 1.4.2
99
otp_release:
1010
- 18.3
1111
- 19.0
1212
- 19.1
1313
- 19.2
14-
matrix:
15-
exclude:
16-
- otp_release: 19.0
17-
elixir: 1.2.5
18-
- otp_release: 19.1
19-
elixir: 1.2.5
20-
- otp_release: 19.2
21-
elixir: 1.2.5
14+
- 19.3
2215
addons:
2316
postgresql: "9.4"
2417
before_script:

CHANGELOG.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,28 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## 2.0.0 - 2017-04-11
8+
- Rewrite the `Trans` module to use underscore functions to store configuration.
9+
- Rewrite the `QueryBuilder` module to unify previous functions into a single macro with compile time checks. Translations can now be used directly when building queries and are compatible with functions and macros provided by `Ecto.Query` and `Ecto.Query.Api`.
10+
- Update the `Translator` module to use the new underscore functions.
11+
- Update documentation and improve the tests.
12+
13+
## 1.1.0 - 2017-02-28
14+
- Make `Ecto` an optional dependency. If `Ecto` is not available the `QueryBuilder` module will not be compiled.
15+
716
## 1.0.2 - 2017-02-19
8-
### Added
917
- Remove `earmark` as a direct dependency since it is already required by `ex_doc`.
1018
- Remove warnings when compiling with Elixir 1.4.
1119
- Adds contribution guidelines detailed in `CONTRIBUTING.md`.
1220

1321
## 1.0.1 - 2016-10-22
14-
### Added
1522
- New testing environments for Travis CI.
1623
- The project has now a changelog.
1724
- Improved documentation.
1825
- Improved README.
1926

2027
## 1.0.0 - 2016-07-30
21-
### Added
2228
- Support for Ecto 2.0.
2329

2430
## 0.1.0 - 2016-06-04
25-
### Added
2631
- Initial release with basic functionality and documentation.

README.md

Lines changed: 62 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use the `Trans.Translator` component without those dependencies though.
2222
- PostgreSQL 9.4 or higher (since `Trans` leverages the JSONB datatype)
2323

2424
Support for MySQL JSON type (introduced in MySQL 5.7) will come also, but right
25-
now it is not yet implemented.
25+
now it is not yet implemented at the database adapter level.
2626

2727
## Why Trans?
2828

@@ -46,9 +46,9 @@ translatable schema has an extra column that contains all of its translations.
4646
This approach drastically reduces the number of required JOINs when filtering or
4747
fetching records.
4848

49-
Trans is lightweight and modularized. The main functionality is provided by the
50-
`Trans.Translator` and the `Trans.QueryBuilder` modules, while the `Trans` module
51-
simplifies the calls to translator and query builder functions from a schema.
49+
`Trans` is lightweight and modularized. The `Trans` module provides metadata
50+
that is used by the `Trans.Translator` and `Trans.QueryBuilder` modules, which
51+
implement the main functionality of this library.
5252

5353
## Making a schema translatable
5454

@@ -84,6 +84,22 @@ defmodule Article do
8484
end
8585
```
8686

87+
Then we must use `Trans` from our schema module to indicate which fields will
88+
be translated.
89+
90+
```elixir
91+
defmodule Article do
92+
use Ecto.Schema
93+
use Trans, translates: [:title, :body]
94+
95+
schema "articles" do
96+
field :title, :string
97+
field :body, :string
98+
field :translations, :map
99+
end
100+
end
101+
```
102+
87103
## Storing translations
88104

89105
Translations are stored as a map of maps in the *translation container* of our
@@ -111,45 +127,44 @@ iex> article = Repo.insert!(changeset)
111127
## Filtering queries by translations
112128

113129
We may want to fetch articles that are translated into a certain language. To
114-
do this we may use the `with_translations/3` function of the `Trans.QueryBuilder`
115-
module:
130+
do this we use the `Trans.QueryBuilder.translated/3` macro, which generates the
131+
required SQL fragment for us.
116132

117133
```elixir
118-
iex> Article
119-
...> |> Trans.QueryBuilder.with_translations(:es)
120-
...> |> Repo.all
121-
[debug] SELECT a0."id", a0."title", a0."body", a0."translations"
122-
FROM "articles" AS a0
123-
WHERE ((a0."translations"->>$1) is not null) ["es"]
124-
[debug] OK query=4.7ms queue=0.1ms
134+
iex> Repo.all(from a in Article,
135+
...> where: not is_nil(translated(Article, a, :es)))
136+
# SELECT a0."id", a0."title", a0."body", a0."translations"
137+
# FROM "articles" AS a0
138+
# WHERE (NOT ((a0."translations"->"es") IS NULL))
125139
```
126140

127141
We can also get more specific and fetch only those articles for which their
128-
Spanish title contains the word "Trans".
142+
Spanish title matches "Elixir".
129143

130144
```elixir
131-
iex> Article
132-
...> |> Trans.QueryBuilder.with_translation(:es, :title, "Trans")
133-
...> |> Repo.all
134-
[debug] SELECT a0."id", a0."title", a0."body", a0."translations"
135-
FROM "articles" AS a0
136-
WHERE (a0."translations"->$1->>$2 = $3) ["es", "title", "Trans"]
137-
[debug] OK query=2.6ms queue=0.1ms
145+
iex> Repo.all(from a in Article,
146+
...> where: translated(Article, a.title, :es) == "Elixir")
147+
# SELECT a0."id", a0."title", a0."body", a0."translations"
148+
# FROM "articles" AS a0
149+
# WHERE ((a0."translations"->"fr"->>"title") = "Elixir")
138150
```
139151

140-
By default `Trans` looks for an exact match when we add a condition. We may
141-
also perform a LIKE or ILIKE comparison and use wilcards like this:
152+
The SQL fragment generated by the `Trans.QueryBuilder.translated/3` macro is
153+
compatible with the rest of functions and macros provided by `Ecto.Query` and
154+
`Ecto.Query.Api`.
142155

143156
```elixir
144-
iex> Article
145-
...> |> Trans.QueryBuilder.with_translation(:es, :title, "%Trans%", type: :like)
146-
...> |> Repo.all
147-
[debug] SELECT a0."id", a0."title", a0."body", a0."translations"
148-
FROM "articles" AS a0
149-
WHERE (a0."translations"->$1->>$2 LIKE $3) ["es", "title", "%Trans%"]
150-
[debug] OK query=2.1ms queue=0.1ms
157+
iex> Repo.all(from a in Article,
158+
...> where: ilike(translated(Article, a.body, :es), "%elixir%"))
159+
# SELECT a0."id", a0."title", a0."body", a0."translations"
160+
# FROM "articles" AS a0
161+
# WHERE ((a0."translations"->"es"->>"body") ILIKE "%elixir%")
151162
```
152163

164+
More complex queries such as adding conditions to joined schemas can be easily
165+
generated in the same way. Take a look at the documentation and tests for more
166+
examples.
167+
153168
## Obtaining translations from a struct
154169

155170
In those examples we will be referring to this article:
@@ -171,20 +186,20 @@ iex> article = %Article{
171186
...> }
172187
```
173188

174-
Once we have already loaded a struct, we may use the `Trans.Translator.translate/4`
175-
function to easily access a translation for a certain field.
189+
Once we have already loaded a struct, we may use the `Trans.Translator.translate/3`
190+
function to easily access a translation of a certain field.
176191

177192
```elixir
178-
iex> Article.translate(article, :es, :body)
193+
iex> Trans.Translator.translate(article, :title, :es)
179194
"Cómo escribir un corrector ortográfico"
180195
```
181196

182-
The `Trans.Translator.translate/4` function also provides a fallback mechanism
197+
The `Trans.Translator.translate/3` function also provides a fallback mechanism
183198
that activates when the required translation does not exist:
184199

185200
```elixir
186-
iex> Article.translate(article, :de, :title)
187-
"How to Write a Spelling Corrector" # Fallback to untranslated value
201+
iex> Trans.Translator.translate(article, :title, :de)
202+
"How to Write a Spelling Corrector"
188203
```
189204

190205
## Using a different *translation container*
@@ -193,42 +208,13 @@ In the previous examples we have used `translations` as the name of the
193208
*translation container* and `Trans` looks automatically for translations into this
194209
field.
195210

196-
We can also give the *translation container* a different name:
197-
198-
```elixir
199-
defmodule Article do
200-
use Ecto.Schema
201-
202-
schema "articles" do
203-
field :title, :string
204-
field :body, :string
205-
field :article_translations, :map # this is our translation container
206-
end
207-
end
208-
```
209-
210-
We can call the same functions as in previous examples, but we have to specify
211-
the name of the *translation container* to override the default:
212-
213-
```elixir
214-
iex> Article
215-
...> |> Trans.QueryBuilder.with_translation(:es, :title, "Trans", container: :article_translations)
216-
...> |> Repo.all
217-
[debug] SELECT a0."id", a0."title", a0."body", a0."translations"
218-
FROM "articles" AS a0
219-
WHERE (a0."article_translations"->$1->>$2 = $3) ["es", "title", "Trans"]
220-
[debug] OK query=2.6ms queue=0.1ms
221-
```
222-
223-
Having to specify the name of the *translation container* everytime is error
224-
prone and can become tiresome. Instead we can use the `Trans` module in our
225-
schema and have this option specified automatically for us:
211+
We can also give the *translation container* a different name, for example
212+
**article_translations**:
226213

227214
```elixir
228215
defmodule Article do
229216
use Ecto.Schema
230-
use Trans, defaults: [container: :article_translations],
231-
translates: [:title, :body]
217+
use Trans, translates: [:title, :body], container: :article_translations
232218

233219
schema "articles" do
234220
field :title, :string
@@ -238,14 +224,13 @@ defmodule Article do
238224
end
239225
```
240226

241-
Now we can do:
227+
We can call the same functions as in previous examples and both `Trans.Translator`
228+
and `Trans.QueryBuilder` will automatically look for translations in the correct field.
242229

243230
```elixir
244-
iex> Article
245-
...> |> Article.with_translation(:es, :title, "Trans")
246-
...> |> Repo.all
247-
[debug] SELECT a0."id", a0."title", a0."body", a0."translations"
248-
FROM "articles" AS a0
249-
WHERE (a0."article_translations"->$1->>$2 = $3) ["es", "title", "Trans"]
250-
[debug] OK query=2.6ms queue=0.1ms
251-
```
231+
iex> Repo.all(from a in Article,
232+
...> where: not is_nil(translated(Article, a, :es)))
233+
# SELECT a0."id", a0."title", a0."body", a0."article_translations"
234+
# FROM "articles" AS a0
235+
# WHERE (NOT ((a0."article_translations"->"es") IS NULL))
236+
```

0 commit comments

Comments
 (0)