t_t 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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