t_t 1.0.1 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 93be7cafdab979b4837cf9be239ecdec38ed2d04
4
- data.tar.gz: a3bc394c6c54755306a3108e133e4f1f1b5cb24a
3
+ metadata.gz: b73436334b6c70340cf56c69878f5645ddecbd12
4
+ data.tar.gz: 6f9f3913be2190fc19abce30109a1816621562b8
5
5
  SHA512:
6
- metadata.gz: b680bb07574bac2214318dd97059757e21a4e7bfae91d6ed32602c828c79cbac1b1ba98e8301e63f35ece020d6a7e5a334dde3da560960df9bb15911dc054355
7
- data.tar.gz: 789a592362b858ac87dd2e564c8cc33e83e6eeae6a926b5617bad58bc7b75e70386620e9c2b6029a7179ec207a43651a418906a3e0af85d330b775db2629e3f1
6
+ metadata.gz: a9c423a3ab34a17a0f4f858a773d7c0d60250e32f18679e469e136de30b22b68b5fe5af7e30820c83fbedbcd47d097a65807050989ec875422daa0ba577946ad
7
+ data.tar.gz: 42c316f2a5355a2b8379724847f8088582c3f46782811fff42595f6e49f7795ac711dfefe090d41f225db956c08507d02d4cb7a598bdfcbf900d9d393d787ab7
data/Rakefile CHANGED
@@ -7,4 +7,10 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.pattern = 'tests/**/*_test.rb'
8
8
  end
9
9
 
10
+ Rake::TestTask.new(:run) do |t|
11
+ t.libs << "lib"
12
+ t.libs << "tests"
13
+ t.pattern = ARGV[1]
14
+ end
15
+
10
16
  task default: :test
data/cheatsheet.md CHANGED
@@ -84,8 +84,7 @@ You probably know that `active_model` based classes have handy method **#human_a
84
84
  ```ruby
85
85
  # en:
86
86
  # attributes:
87
- # base:
88
- # email: "Email"
87
+ # email: "Email"
89
88
  # user:
90
89
  # email: "Login / Email"
91
90
 
@@ -241,12 +240,10 @@ comes on the scene:
241
240
  ```ruby
242
241
  # en:
243
242
  # actions:
244
- # create:
245
- # base: "The %{r} has been created"
246
- # update:
247
- # base: "The %{r} has been updated"
248
- # delete:
249
- # base: "The %{r} has been deleted"
243
+ # base:
244
+ # create: "The %{r} has been created"
245
+ # update: "The %{r} has been updated"
246
+ # delete: "The %{r} has been deleted"
250
247
  # models:
251
248
  # user:
252
249
  # one: "User"
@@ -286,14 +283,17 @@ just add a model name key in a action translations:
286
283
  ```ruby
287
284
  # en:
288
285
  # actions:
289
- # create:
290
- # base: "The %{r} has been created"
291
- # photo: "The %{r} has been uploaded"
286
+ # base:
287
+ # create: "The %{r} has been created"
288
+ # photo:
289
+ # create: "The %{r} has been uploaded"
292
290
 
293
291
  tt.a(:create, :user) # => "The user has been created"
294
292
  tt.a(:create, :photo) # => "The photo has been uploaded"
295
293
  ```
296
- Take a look at [examples folder](./examples/) for a more examples.
294
+
295
+ To simplify generation of translations Dos-T provides [the action factory](./docs/action_factory.md).
296
+ For more examples take a look [here](./examples/).
297
297
 
298
298
  ## Custom shortcuts
299
299
 
@@ -0,0 +1,179 @@
1
+ # Overview
2
+
3
+ Dos-T provides a factory (not required by default) to generate 'action'-translations in a few lines.
4
+
5
+ ```ruby
6
+ # config/locales/actions.rb
7
+ require 't_t/action_factory'
8
+
9
+ TT.define_actions(:en, :de) do |f|
10
+ f.add :see_all, en: "See all %{rs}", de: "Siehe alle %{RS}"
11
+ end
12
+ ```
13
+
14
+ From the example above you've understood the basic DSL's api, but started wondering what's a point of it due to fill
15
+ a yml file will be faster and easier. The reason comes when you will face a grammar rule which requires a different
16
+ texts. The most popular case is an English "a/an" rule. For example, an application has the next translation:
17
+
18
+ ```ruby
19
+ # config/locales/actions.en.yml
20
+ en:
21
+ actions:
22
+ base:
23
+ choose: "Please, choose a %{r}"
24
+ agent:
25
+ choose: "Please, choose an %{r}"
26
+ article:
27
+ choose: "Please, choose an %{r}"
28
+ occupation:
29
+ choose: "Please, choose an %{r}"
30
+ #...
31
+ ```
32
+
33
+ This grammar rule forces us to create n * m keys where:
34
+ - n is a count of actions with the rule
35
+ - m is a count of models which uses "an"
36
+ A plain translation file becomes large what makes the its maintenance hard. With Dos-T it's easy due to you
37
+ can teach DSL some grammar and it will generates all translation for you:
38
+
39
+ ```ruby
40
+ # config/locales/actions.rb
41
+ require 't_t/action_factory'
42
+
43
+ TT.define_actions(:en) do |f|
44
+ f.for(:en) do |l|
45
+ # defines `a/an` rule for English where:
46
+ # base - a base action translation or a result of the previous rule processing
47
+ # a_meta - a action-related metadata (could be specified on adding an action)
48
+ # r_meta - a resource-related metadata (could be specified on marking a resource to use a rule)
49
+ l.rule(:an) { |base, a_meta, r_meta| a_meta }
50
+
51
+ # registers a resources which should use the rule
52
+ l.use_rule_for(:an, :agent, :article, occupation: "a useless resource (`occupation`) metadata for the `an` rule")
53
+ end
54
+
55
+ # "Please, choose an %{r}" is an action metadata for the rule
56
+ f.add :choose, en: f.with_rules("Please, choose a %{r}", an: "Please, choose an %{r}")
57
+ f.add :create, en: f.with_rules("Create a %{r}", an: "Create an %{r}")
58
+ f.add :delete_all, en: "Do you want to delete all %{rs}"
59
+ end
60
+ ```
61
+
62
+ Here an another example with a more complex grammar rules:
63
+
64
+ ```ruby
65
+ require 't_t/action_factory'
66
+ # de:
67
+ # models:
68
+ # article:
69
+ # one: "Artikel"
70
+ # other: "Artikel"
71
+ # child:
72
+ # one: "Kind"
73
+ # other: "Kinder"
74
+ # comment:
75
+ # one: "Komment"
76
+ # other: "Komments"
77
+ # list:
78
+ # one: "Liste"
79
+ # other: "Listen"
80
+ # log:
81
+ # one: "Protokoll"
82
+ # other: "Protokolle"
83
+ # person:
84
+ # one: "Person"
85
+ # other: "Personen"
86
+ # ru:
87
+ # models:
88
+ # article:
89
+ # one: "Статья"
90
+ # other: "Статьи"
91
+ # child:
92
+ # one: "Ребенок"
93
+ # other: "Дети"
94
+ # comment:
95
+ # one: "Комментарий"
96
+ # other: "Комментарии"
97
+ # list:
98
+ # one: "Список"
99
+ # other: "Списки"
100
+ # log:
101
+ # one: "Запись"
102
+ # other: "Записи"
103
+ # person:
104
+ # one: "Персона"
105
+ # other: "Персоны"
106
+
107
+ TT.define_actions(:de, :ru) do |f|
108
+ f.for(:de) do |l|
109
+ # German articles are sensitive to gender
110
+ l.rule(:feminine) { |_, a_meta, _| a_meta }
111
+ l.rule(:neuter) { |_, a_meta, _| a_meta }
112
+
113
+ l.use_rule_for(:feminine, :list, :person)
114
+ l.use_rule_for(:neuter, :child, :log)
115
+ end
116
+
117
+ f.for(:ru) do |l|
118
+ # Russian language has 6 noun cases(падежи)
119
+ # for passive voice Accuse case(Винительный падеж - кого? что?) is used
120
+ l.rule(:accuse) { |base, _, r_meta| r_meta.inject(base) { |str, (k, v)| str.gsub("%{#{ k }}", v) } }
121
+ l.use_rule_for :accuse,
122
+ article: { r: "статью", R: "Статью" }, # the plural version is not changing
123
+ child: { r: "ребенка", rs: "детей", R: "Ребенка", RS: "Детей" },
124
+ person: { r: "персону", rs: "персон", R: "Персону", RS: "Персон" }
125
+ end
126
+
127
+ f.add :add,
128
+ de: f.with_rules("Neuen %{R} hinzufügen", feminine: "Neue %{R} hinzufügen", neuter: "Neues %{R} hinzufügen"),
129
+ ru: f.with_rules("Добавить %{r}", :accuse)
130
+
131
+ f.add :edit,
132
+ de: f.with_rules("Den %{R} bearbeiten", feminine: "Die %{R} bearbeiten", neuter: "Das %{R} bearbeiten"),
133
+ ru: f.with_rules("Изменить %{r}", :accuse)
134
+
135
+ f.add :delete_all,
136
+ de: "Alle %{RS} löschen"
137
+ ru: f.with_rules("Удалить %{rs}", :accuse)
138
+ end
139
+ ```
140
+
141
+ The generated list of actions is present in the following table:
142
+
143
+ ||add-de|edit-de|delete-de|add-ru|edit-ru|delete-ru|
144
+ |---|---|---|---|---|---|---|
145
+ |article|Neuen Artikel hinzufügen|Den Artikel bearbeiten|Alle Artikel löschen|Добавить статью|Изменить статью|Удалить статьи|
146
+ |child|Neues Kind hinzufügen|Das Kind bearbeiten|Alle Kinder löschen|Добавить ребенка|Изменить ребенка|Удалить детей|
147
+ |comment|Neuen Komment hinzufügen|Den Komment bearbeiten|Alle Komments löschen|Добавить комментарий|Изменить комментарий|Удалить комментарии|
148
+ |list|Neue Liste hinzufügen|Die Liste bearbeiten|Alle Listen löschen|Добавить список|Изменить список|Удалить списки|
149
+ |log|Neues Protokoll hinzufügen|Das Protokoll bearbeiten|Alle Protokolle löschen|Добавить запись|Изменить запись|Удалить записи|
150
+ |person|Neue Person hinzufügen|Die Person bearbeiten|Alle Personen löschen|Добавить персону|Изменить персону|Удалить персон|
151
+
152
+ With the factory you get a flexibility in resource renaming - just change of a few lines. A possibility to make a typo
153
+ in a similar translations down to zero. All shown rules a included in the factory, but not activated.
154
+ To do it, do the next:
155
+
156
+ ```ruby
157
+ TT.define_actions(:en, :de, :ru) do |f|
158
+ # the naming convention is similar to BEM
159
+ f.activate_rules(:en__an, :de__gender, :ru__accuse)
160
+ end
161
+ ```
162
+
163
+ If you would like to add another grammar rule feel free to make a pull request.
164
+
165
+ ## Adding an exceptions
166
+
167
+ Dos-T was built to maximize the usage of patterns in translations. It helps to avoid typos and long files, but texts
168
+ become more technical. In the previous section, you probably noticed that translations like "Das Kind bearbeiten"
169
+ (change the child) or "Изменить персону"(change the person) better to replace by more human oriented
170
+ "Das Kind-Profile bearbeiten" (change the child's profile) and "Изменить биографию персоны" (change the person's bio).
171
+ For those situations the library allows to specify an exceptions:
172
+
173
+ ```ruby
174
+ TT.define_actions(:de, :ru) do |f|
175
+ # declaration of rules and actions
176
+
177
+ f.add_exception(:child, ru: { edit: "Das Kind-Profile bearbeiten" }, de: { edit: "Изменить профиль ребенка" })
178
+ end
179
+ ```
@@ -101,11 +101,11 @@ en:
101
101
  blank: Select at least one task list
102
102
  # action related
103
103
  actions:
104
- choose_few:
105
- base: Choose one or more %{rs}
106
- create:
107
- base: "The %{r} has been created"
108
- task: "The %{r} hass been added"
109
- select_before:
110
- base: "Please choose a %{r} before"
111
- icon: "Please choose an %{r} before"
104
+ base:
105
+ choose_few: "Choose one or more %{rs}"
106
+ create: "The %{r} has been created"
107
+ select_before: "Please choose a %{r} before"
108
+ task:
109
+ create: "The %{r} hass been added"
110
+ icon:
111
+ select_before: "Please choose an %{r} before"
@@ -0,0 +1,137 @@
1
+ require 't_t/builtin_rules'
2
+
3
+ module TT
4
+ class ActionFactory
5
+ Action = Struct.new(:base, :rules)
6
+ Option = Struct.new(:key, :meta) do
7
+ def self.parse(list)
8
+ list.flat_map do |item|
9
+ item.respond_to?(:map) ? item.map { |key, meta| new(key, meta) } : new(item)
10
+ end
11
+ end
12
+ end
13
+
14
+ class Locale
15
+ def initialize
16
+ @rules = {}
17
+ @list = {}
18
+ end
19
+
20
+ def rule(key, &block)
21
+ @rules[key] = block
22
+ @list[key] = []
23
+ end
24
+
25
+ def use_rule_for(key, *list)
26
+ @list[key].concat(Option.parse(list))
27
+ end
28
+
29
+ def knows_rule?(key)
30
+ @rules.has_key?(key)
31
+ end
32
+
33
+ def compile(action)
34
+ action.rules.inject(base: action.base) do |result, a_option|
35
+ rule = @rules.fetch(a_option.key)
36
+
37
+ @list.fetch(a_option.key).each do |r_option|
38
+ base = result.fetch(r_option.key, action.base)
39
+ result[r_option.key] = rule.call(base, a_option.meta, r_option.meta)
40
+ end
41
+
42
+ result
43
+ end
44
+ end
45
+ end
46
+
47
+ def initialize(*locales)
48
+ @actions = {}
49
+ @locales = {}
50
+ @exceptions = {}
51
+
52
+ locales.each do |lkey|
53
+ @actions[lkey] = {}
54
+ @exceptions[lkey] = {}
55
+ @locales[lkey] = Locale.new
56
+ end
57
+ end
58
+
59
+ def for(key, &block)
60
+ yield @locales.fetch(key) { raise_error "`#{ key }` is unknown" }
61
+ end
62
+
63
+ def activate_rules(*list)
64
+ list.each { |rkey| BuiltinRules.send(rkey, self) }
65
+ end
66
+
67
+ def add(akey, list)
68
+ @locales.each do |lkey, locale|
69
+ unless action = list[lkey]
70
+ raise_error "action `#{ akey }` is missing for `#{ lkey }` locale"
71
+ end
72
+
73
+ action = Action.new(action, []) if action.is_a?(String)
74
+
75
+ if action.is_a?(Action)
76
+ action.rules.each do |rule|
77
+ next if locale.knows_rule?(rule.key)
78
+ raise_error "`#{ rule.key }` is an unknown rule for `#{ lkey }` locale"
79
+ end
80
+ else
81
+ raise_error "the value of `#{ akey }` action for `#{ lkey }` locale has a wrong type"
82
+ end
83
+
84
+ @actions[lkey][akey] = action
85
+ end
86
+ end
87
+
88
+ def with_rules(base, *list)
89
+ Action.new(base, Option.parse(list))
90
+ end
91
+
92
+ def add_exception(mkey, schema)
93
+ schema.each do |lkey, list|
94
+ raise_error("`#{ lkey }` is an unknown locale") unless @locales.has_key?(lkey)
95
+
96
+ list.each do |akey, str|
97
+ unless @actions[lkey].has_key?(akey)
98
+ raise_error "`#{ akey }` action is not specified. Do it before add an exception"
99
+ end
100
+
101
+ @exceptions[lkey][akey] ||= {}
102
+ @exceptions[lkey][akey][mkey] = str
103
+ end
104
+ end
105
+ end
106
+
107
+ def as_hash
108
+ @actions.inject({}) do |hash, (lkey, list)|
109
+ locale = @locales.fetch(lkey)
110
+
111
+ actions = list.inject({}) do |result, (akey, action)|
112
+ keys = locale.compile(action).merge!(@exceptions[lkey].fetch(akey, {}))
113
+ keys.each do |mkey, str|
114
+ result[mkey] = {} unless result.has_key?(mkey)
115
+ result[mkey][akey] = str
116
+ end
117
+
118
+ result
119
+ end
120
+
121
+ hash.merge!(lkey => { actions: actions })
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def raise_error(base)
128
+ raise ArgumentError, "t_t: #{ base }"
129
+ end
130
+ end
131
+
132
+ def self.define_actions(*args)
133
+ f = ActionFactory.new(*args)
134
+ yield f
135
+ f.as_hash
136
+ end
137
+ end
@@ -0,0 +1,45 @@
1
+ module TT
2
+ module BuiltinRules
3
+ extend self
4
+
5
+ # The indefinite article a (before a consonant sound) or an (before a vowel sound)
6
+ # is used only with singular, countable nouns.
7
+ def en__an(f)
8
+ f.for(:en) do |l|
9
+ l.rule(:an) { |_, a_meta, _| a_meta }
10
+ end
11
+ end
12
+
13
+ # The articles in German
14
+ # | masculine | feminine | neuter | plural |
15
+ # | neuen | neue | neues | neue |
16
+ # | keinen | keine | kein | keine |
17
+ # | der | die | das | die |
18
+
19
+ # this lambda generate default translation for masculine form
20
+ # & add exceptions for feminine & neuter genders
21
+ # ie
22
+ # new:
23
+ # base: "Neuen %{r} hinzufügen" -> "Neuen Benutzer anlegen"
24
+ # company: "Neues %{r} hinzufügen" -> "Neues Unternehmen anlegen"
25
+ # role: "Neue %{r} hinzufügen" -> "Neue Rolle hinzufügen"
26
+ def de__gender(f)
27
+ f.for(:de) do |l|
28
+ l.rule(:feminine) { |_, a_meta, _| a_meta }
29
+ l.rule(:neuter) { |_, a_meta, _| a_meta }
30
+ end
31
+ end
32
+
33
+ # To get a correct translation in Russian
34
+ # you need to set the proper ending for object by using `Винительный падеж - Кого? Что?`
35
+ # "Создать Компанию(кого?) & Cоздать Сектор(что?)"
36
+ # for `что?` we can use the resource name, for `кого?` - need to provide a separated key
37
+ def ru__accuse(f)
38
+ f.for(:ru) do |l|
39
+ l.rule(:accuse) do |base, _, r_meta|
40
+ r_meta.inject(base) { |str, (k, t)| str.gsub("%{#{k}}", t) }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
data/lib/t_t.rb CHANGED
@@ -9,7 +9,7 @@ module TT
9
9
 
10
10
  DOWNCASE = lambda { |str, locale| (locale == :en) ? str.downcase : str.mb_chars.downcase.to_s }
11
11
 
12
- def lookup(prefix, base_suffix = :base)
12
+ def lookup(prefix, base_suffix)
13
13
  prefix ? prefix_lookup(prefix, base_suffix) : simple_lookup(base_suffix)
14
14
  end
15
15
 
@@ -28,7 +28,11 @@ module TT
28
28
 
29
29
  defaults = []
30
30
  defaults << :"#{ type }.#{ parts.last }" if parts.length > 1
31
- defaults << :"#{ type }.#{ base_suffix }"
31
+ if base_suffix
32
+ defaults << :"#{ type }.#{ base_suffix }"
33
+ else
34
+ defaults << type
35
+ end
32
36
 
33
37
  [root, defaults]
34
38
  end
@@ -47,8 +51,14 @@ module TT
47
51
  defaults << :"#{ prefix }.#{ type }.#{ pure_model }"
48
52
  defaults << :"#{ type }.#{ pure_model }"
49
53
  end
50
- defaults << :"#{ prefix }.#{ type }.#{ base_suffix }"
51
- defaults << :"#{ type }.#{ base_suffix }"
54
+
55
+ if base_suffix
56
+ defaults << :"#{ prefix }.#{ type }.#{ base_suffix }"
57
+ defaults << :"#{ type }.#{ base_suffix }"
58
+ else
59
+ defaults << :"#{ prefix }.#{ type }"
60
+ defaults << type
61
+ end
52
62
 
53
63
  [root, defaults]
54
64
  end
@@ -85,22 +95,25 @@ module TT
85
95
  lookup_key_method :c, :common
86
96
 
87
97
  def initialize(ns, section = nil)
88
- @lookup = Utils.lookup(self.class.settings[:prefix])
89
- @err_lookup = Utils.lookup(self.class.settings[:prefix], :messages)
98
+ @lookup = Utils.lookup(self.class.settings[:prefix], nil)
99
+ @b_lookup = Utils.lookup(self.class.settings[:prefix], :base)
100
+ @e_lookup = Utils.lookup(self.class.settings[:prefix], :messages)
90
101
 
91
102
  ns = Utils.to_parts(ns).join('.')
92
103
  @config = { ns: ns, root: (section ? "#{ ns }.#{ section }" : ns) }
93
104
  default_model = ns.to_s.singularize
94
- [:actions, :attributes, :enums, :models].each do |i|
95
- @config[i] = @lookup.call(default_model, i)
96
- end
97
- @config[:errors] = @err_lookup.call(default_model, :errors)
105
+
106
+ @config[:attributes] = @lookup.call(default_model, :attributes)
107
+ @config[:models] = @lookup.call(default_model, :models)
108
+ @config[:actions] = @b_lookup.call(default_model, :actions)
109
+ @config[:enums] = @b_lookup.call(default_model, :enums)
110
+ @config[:errors] = @e_lookup.call(default_model, :errors)
98
111
 
99
112
  @downcase = self.class.settings.fetch(:downcase, Utils::DOWNCASE)
100
113
  end
101
114
 
102
115
  def a(name, model_name = nil, custom = {})
103
- path, defaults = _resolve(model_name, :actions, name)
116
+ path, defaults = _resolve(@b_lookup, model_name, :actions, name)
104
117
 
105
118
  resource = r(model_name)
106
119
  resources = rs(model_name)
@@ -111,12 +124,12 @@ module TT
111
124
  end
112
125
 
113
126
  def attr(name, model_name = nil)
114
- path, defaults = _resolve(model_name, :attributes, name)
127
+ path, defaults = _resolve(@lookup, model_name, :attributes, name)
115
128
  I18n.t path, default: defaults
116
129
  end
117
130
 
118
131
  def enum(name, kind, model_name = nil)
119
- path, defaults = _resolve(model_name, :enums, "#{ name }.#{ kind }")
132
+ path, defaults = _resolve(@b_lookup, model_name, :enums, "#{ name }.#{ kind }")
120
133
  I18n.t path, default: defaults
121
134
  end
122
135
 
@@ -132,8 +145,9 @@ module TT
132
145
  end
133
146
 
134
147
  def rs(model_name = nil, count = 10)
135
- path, defaults = _resolve(model_name, :models)
136
- I18n.t path, default: defaults, count: count
148
+ path, defaults = _resolve(@lookup, model_name, :models, nil)
149
+ # cut from defaults :"#{ orm }.models", :models
150
+ I18n.t path, default: defaults[0...-2], count: count
137
151
  end
138
152
 
139
153
  def t(key, custom = {})
@@ -147,21 +161,17 @@ module TT
147
161
  @config
148
162
  end
149
163
 
150
- def _resolve(model_name, type, key = nil)
151
- _resolve_with_lookup(@lookup, model_name, type, key)
152
- end
153
-
154
164
  def _resolve_errors(model_name, attr_name, error_name)
155
165
  if attr_name == :base
156
- _resolve_with_lookup(@err_lookup, model_name, :errors, error_name)
166
+ _resolve(@e_lookup, model_name, :errors, error_name)
157
167
  else
158
- path, _defaults = _resolve(model_name, :errors, "#{ attr_name }.#{ error_name }")
168
+ path, _defaults = _resolve(@lookup, model_name, :errors, "#{ attr_name }.#{ error_name }")
159
169
  defaults = _defaults + ["errors.messages.#{ error_name }".to_sym]
160
170
  return path, defaults
161
171
  end
162
172
  end
163
173
 
164
- def _resolve_with_lookup(lookup, model_name, type, key)
174
+ def _resolve(lookup, model_name, type, key)
165
175
  paths = model_name ? lookup.call(model_name, type) : _config.fetch(type)
166
176
  if key
167
177
  return "#{ paths.first }.#{ key }", paths.last.map { |i| :"#{ i }.#{ key }" }
data/readme.md CHANGED
@@ -17,8 +17,8 @@ is to look at [Cheatsheet](./cheatsheet.md). The below is shown a brief overview
17
17
  ```Haml
18
18
  # en:
19
19
  # actions:
20
- # add:
21
- # base: "Add a new %{r}"
20
+ # base:
21
+ # add: "Add a new %{r}"
22
22
  # attributes:
23
23
  # user:
24
24
  # name: "Name"
@@ -94,3 +94,12 @@ Just add `gem "t_t"` into your Gemfile and run `bundle`.
94
94
  ## Requirements
95
95
 
96
96
  Dos-T is tested against Ruby 1.9.3+. If your application uses Ruby on Rails the framework version should be 3.2+
97
+
98
+ ## Changelog
99
+
100
+ - 1.1.0:
101
+ - Added [the action factory](./docs/action_factory.md)
102
+ - Improve #attr, #r, #rs methods to make them more compatible with ActiveModel methods
103
+ - Fix a documentation mismatching
104
+ - 1.0.1
105
+ - fix the activerecord integration
data/t_t.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "t_t"
5
- spec.version = "1.0.1"
5
+ spec.version = "1.1.0"
6
6
  spec.authors = ["Sergey Pchelintsev"]
7
7
  spec.email = ["mail@sergeyp.me"]
8
8
  spec.summary = %q{An opinioned I18n helper}
@@ -0,0 +1,60 @@
1
+ require 'test_helper'
2
+
3
+ describe "Action factory" do
4
+ describe "adding an actions" do
5
+ it 'adds an action' do
6
+ result = factory(:es) { |f| f.add :sing, es: 'cantar' }
7
+ assert_equal result, { es: { actions: { base: { sing: 'cantar' } } } }
8
+ end
9
+
10
+ it 'checks a locale action presence' do
11
+ assert_raises(ArgumentError, 't_t: action `run` is missing for `fr` locale') do
12
+ factory(:fr) { |f| f.add :run, en: 'Run' }
13
+ end
14
+ end
15
+
16
+ it 'checks a locale rule presence' do
17
+ assert_raises(ArgumentError, 't_t: `feminine` is an unknown rule for `es` locale') do
18
+ factory(:es) { |f| f.add :swim, es: f.with_rules('El nada', feminine: 'Ella nada') }
19
+ end
20
+ end
21
+
22
+ it 'checks a valid action type' do
23
+ assert_raises(ArgumentError, 't_t: the value of `count` action for `fr` locale has a wrong type') do
24
+ factory(:fr) { |f| f.add :count, fr: 34 }
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "adding an exceptions" do
30
+ it 'adds an exception' do
31
+ result = factory(:en) do |f|
32
+ f.add :add, en: 'Add a new'
33
+ f.add_exception :user, en: { add: 'Register a new' }
34
+ end
35
+
36
+ assert_equal result, { en: { actions: { base: { add: 'Add a new' }, user: { add: 'Register a new' } } } }
37
+ end
38
+
39
+ it 'checks a locale presence' do
40
+ assert_raises(ArgumentError, 't_t: `ru` is an unknown locale') do
41
+ factory(:en) do |f|
42
+ f.add :add, en: 'Add a new'
43
+ f.add_exception :user, ru: { add: 'Register a new' }
44
+ end
45
+ end
46
+ end
47
+
48
+ it 'checks an action presence' do
49
+ assert_raises(ArgumentError, 't_t: `listen` action is not specified. Do it before add an exception') do
50
+ factory(:en) { |f| f.add_exception :visitor, en: { listen: "listen to a band" } }
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def factory(*args, &block)
58
+ TT.define_actions(*args, &block)
59
+ end
60
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ describe 'Built-in rules' do
4
+ it '#en__an' do
5
+ result = with_factory(:en, :an) do |f|
6
+ f.for(:en) { |l| l.use_rule_for(:an, :alarm) }
7
+
8
+ f.add :add, en: f.with_rules('A', an: 'An')
9
+ end
10
+
11
+ assert_equal result[:base][:add], 'A'
12
+ assert_equal result[:alarm][:add], 'An'
13
+ end
14
+
15
+ it '#de__gender' do
16
+ list = with_factory(:de, :gender) do |f|
17
+ f.for(:de) do |l|
18
+ l.use_rule_for(:feminine, :role)
19
+ l.use_rule_for(:neuter, :company)
20
+ end
21
+
22
+ f.add :choose_gender, de: f.with_rules('M', feminine: 'F', neuter: 'N')
23
+ end
24
+
25
+ assert_equal list[:base][:choose_gender], 'M'
26
+ assert_equal list[:role][:choose_gender], 'F'
27
+ assert_equal list[:company][:choose_gender], 'N'
28
+ end
29
+
30
+ it '#ru__accuse' do
31
+ list = with_factory(:ru, :accuse) do |f|
32
+ f.for(:ru) do |l|
33
+ l.use_rule_for(:accuse, man: { r: 'man', R: 'Man' }, woman: { RS: 'Women', rs: 'women' })
34
+ end
35
+
36
+ f.add :accuse, ru: f.with_rules("%{r} %{rs} %{R} %{RS}", :accuse)
37
+ end
38
+
39
+ assert_equal list[:base][:accuse], "%{r} %{rs} %{R} %{RS}"
40
+ assert_equal list[:man][:accuse], "man %{rs} Man %{RS}"
41
+ assert_equal list[:woman][:accuse], "%{r} women %{R} Women"
42
+ end
43
+
44
+ private
45
+
46
+ def with_factory(lang, macro)
47
+ TT.define_actions(lang) do |f|
48
+ f.activate_rules("#{ lang }__#{ macro }")
49
+ yield f
50
+ end[lang][:actions]
51
+ end
52
+ end
@@ -2,7 +2,7 @@ require 'test_helper'
2
2
 
3
3
  describe 'Methods related to models' do
4
4
  before do
5
- @tt = ARTranslator.new("admin/users", "spec")
5
+ @tt = TT::Translator.new("admin/users", "spec")
6
6
  end
7
7
 
8
8
  describe 'actions' do
@@ -44,7 +44,7 @@ describe 'Methods related to models' do
44
44
  describe 'attributes' do
45
45
  before do
46
46
  load_i18n(attributes: {
47
- base: { name: 'Name', phone: 'Phone', email: 'Email' },
47
+ name: 'Name', phone: 'Phone', email: 'Email',
48
48
  user: { name: 'Nick', phone: 'Notification phone' },
49
49
  admin: { user: { name: 'Contact admin name' } }
50
50
  })
@@ -134,7 +134,7 @@ describe 'Methods related to models' do
134
134
 
135
135
  describe 'resource names' do
136
136
  before do
137
- @tt = ARTranslator.new('public/people')
137
+ @tt = TT::Translator.new('public/people')
138
138
  load_i18n({
139
139
  models: { person: { one: "whatever", other: "whatever" }, user: { one: "User", other: "Users" } },
140
140
  activerecord: { models: {
data/tests/test_helper.rb CHANGED
@@ -1,19 +1,18 @@
1
+ # emulate activerecord presence
2
+ ActiveRecord = nil
3
+
1
4
  require "minitest/autorun"
2
5
  require "minitest/mock"
3
6
  require "rack/test"
4
7
  require "action_controller"
5
8
  require "t_t"
6
9
  require "t_t/action_factory"
7
- require "t_t/action_macros"
8
10
 
11
+ ActiveSupport.run_load_hooks(:active_record, self)
9
12
  ViewTranslator = TT.fork do
10
13
  lookup_key_method :f, :form
11
14
  end
12
15
 
13
- ARTranslator = TT.fork do
14
- settings prefix: :activerecord
15
- end
16
-
17
16
  I18n.backend = I18n::Backend::Simple.new
18
17
 
19
18
  class Minitest::Spec
@@ -25,3 +24,12 @@ class Minitest::Spec
25
24
  I18n.backend.store_translations(:en, data)
26
25
  end
27
26
  end
27
+
28
+ class << Minitest::Spec
29
+ alias :focus :it
30
+
31
+ if ENV.has_key?('FOCUS')
32
+ def it(*args, &block)
33
+ end
34
+ end
35
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: t_t
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Pchelintsev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-19 00:00:00.000000000 Z
11
+ date: 2016-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -120,6 +120,7 @@ files:
120
120
  - Gemfile
121
121
  - Rakefile
122
122
  - cheatsheet.md
123
+ - docs/action_factory.md
123
124
  - examples/simple_app.yml
124
125
  - gemfiles/Gemfile.actionpack-3.1.x
125
126
  - gemfiles/Gemfile.actionpack-3.2.x
@@ -127,9 +128,13 @@ files:
127
128
  - gemfiles/Gemfile.actionpack-4.1.x
128
129
  - gemfiles/Gemfile.actionpack-4.2.x
129
130
  - lib/t_t.rb
131
+ - lib/t_t/action_factory.rb
132
+ - lib/t_t/builtin_rules.rb
130
133
  - readme.md
131
134
  - t_t.gemspec
135
+ - tests/lib/action_factory_test.rb
132
136
  - tests/lib/action_pack_test.rb
137
+ - tests/lib/builtin_rules_test.rb
133
138
  - tests/lib/model_test.rb
134
139
  - tests/lib/view_test.rb
135
140
  - tests/test_helper.rb