tr8n_core 4.0.1

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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +69 -0
  4. data/Rakefile +9 -0
  5. data/config/config.yml +34 -0
  6. data/config/tokens/data.yml +45 -0
  7. data/config/tokens/decorations.yml +37 -0
  8. data/lib/tr8n/application.rb +320 -0
  9. data/lib/tr8n/base.rb +123 -0
  10. data/lib/tr8n/cache.rb +144 -0
  11. data/lib/tr8n/cache_adapters/cdb.rb +74 -0
  12. data/lib/tr8n/cache_adapters/file.rb +70 -0
  13. data/lib/tr8n/cache_adapters/memcache.rb +91 -0
  14. data/lib/tr8n/cache_adapters/redis.rb +94 -0
  15. data/lib/tr8n/component.rb +68 -0
  16. data/lib/tr8n/config.rb +291 -0
  17. data/lib/tr8n/decorators/base.rb +35 -0
  18. data/lib/tr8n/decorators/default.rb +30 -0
  19. data/lib/tr8n/decorators/html.rb +63 -0
  20. data/lib/tr8n/exception.rb +26 -0
  21. data/lib/tr8n/language.rb +250 -0
  22. data/lib/tr8n/language_case.rb +116 -0
  23. data/lib/tr8n/language_case_rule.rb +85 -0
  24. data/lib/tr8n/language_context.rb +115 -0
  25. data/lib/tr8n/language_context_rule.rb +62 -0
  26. data/lib/tr8n/logger.rb +83 -0
  27. data/lib/tr8n/rules_engine/evaluator.rb +156 -0
  28. data/lib/tr8n/rules_engine/parser.rb +83 -0
  29. data/lib/tr8n/source.rb +95 -0
  30. data/lib/tr8n/tokens/data.rb +410 -0
  31. data/lib/tr8n/tokens/data_tokenizer.rb +82 -0
  32. data/lib/tr8n/tokens/decoration_tokenizer.rb +200 -0
  33. data/lib/tr8n/tokens/hidden.rb +48 -0
  34. data/lib/tr8n/tokens/method.rb +52 -0
  35. data/lib/tr8n/tokens/transform.rb +191 -0
  36. data/lib/tr8n/translation.rb +104 -0
  37. data/lib/tr8n/translation_key.rb +205 -0
  38. data/lib/tr8n/translator.rb +62 -0
  39. data/lib/tr8n/utils.rb +124 -0
  40. data/lib/tr8n_core/ext/array.rb +74 -0
  41. data/lib/tr8n_core/ext/date.rb +63 -0
  42. data/lib/tr8n_core/ext/fixnum.rb +39 -0
  43. data/lib/tr8n_core/ext/hash.rb +126 -0
  44. data/lib/tr8n_core/ext/string.rb +44 -0
  45. data/lib/tr8n_core/ext/time.rb +71 -0
  46. data/lib/tr8n_core/generators/cache/base.rb +85 -0
  47. data/lib/tr8n_core/generators/cache/cdb.rb +27 -0
  48. data/lib/tr8n_core/generators/cache/file.rb +69 -0
  49. data/lib/tr8n_core/modules/logger.rb +43 -0
  50. data/lib/tr8n_core/version.rb +27 -0
  51. data/lib/tr8n_core.rb +68 -0
  52. data/spec/application_spec.rb +228 -0
  53. data/spec/base_spec.rb +19 -0
  54. data/spec/config_spec.rb +16 -0
  55. data/spec/decorator_spec.rb +10 -0
  56. data/spec/decorators/base_spec.rb +14 -0
  57. data/spec/decorators/default_spec.rb +12 -0
  58. data/spec/decorators/html_spec.rb +50 -0
  59. data/spec/fixtures/application.json +112 -0
  60. data/spec/fixtures/languages/en-US.json +1424 -0
  61. data/spec/fixtures/languages/es.json +291 -0
  62. data/spec/fixtures/languages/ru.json +550 -0
  63. data/spec/fixtures/translations/ru/basic.json +56 -0
  64. data/spec/fixtures/translations/ru/counters.json +43 -0
  65. data/spec/fixtures/translations/ru/genders.json +171 -0
  66. data/spec/fixtures/translations/ru/last_names.txt +200 -0
  67. data/spec/fixtures/translations/ru/names.json +1 -0
  68. data/spec/fixtures/translations/ru/names.txt +458 -0
  69. data/spec/helper.rb +84 -0
  70. data/spec/language_case_rule_spec.rb +57 -0
  71. data/spec/language_case_spec.rb +58 -0
  72. data/spec/language_context_rule_spec.rb +73 -0
  73. data/spec/language_context_spec.rb +331 -0
  74. data/spec/language_spec.rb +16 -0
  75. data/spec/rules_engine/evaluator_spec.rb +148 -0
  76. data/spec/rules_engine/parser_spec.rb +29 -0
  77. data/spec/tokens/data_spec.rb +160 -0
  78. data/spec/tokens/data_tokenizer_spec.rb +29 -0
  79. data/spec/tokens/decoration_tokenizer_spec.rb +81 -0
  80. data/spec/tokens/hidden_spec.rb +24 -0
  81. data/spec/tokens/method_spec.rb +84 -0
  82. data/spec/tokens/transform_spec.rb +50 -0
  83. data/spec/translation_key_spec.rb +96 -0
  84. data/spec/translation_spec.rb +24 -0
  85. data/spec/utils_spec.rb +64 -0
  86. metadata +176 -0
@@ -0,0 +1,331 @@
1
+ require 'helper'
2
+
3
+ describe Tr8n::LanguageContext do
4
+
5
+ before :all do
6
+ @app = init_application
7
+ @english = @app.language('en-US')
8
+ @russian = @app.language('ru')
9
+ end
10
+
11
+ describe "initialize" do
12
+ it "sets attributes" do
13
+ expect(Tr8n::LanguageContext.attributes).to eq([:language, :keyword, :description, :default_key, :token_expression, :variables, :token_mapping, :keys, :rules])
14
+ end
15
+ end
16
+
17
+ describe "finding context" do
18
+ it "should return correct context" do
19
+ #@user = stub_object(:first_name => "Mike", :gender => "male")
20
+ #@translator = stub_object(:name => "Mike", :user => @user, :gender => "male")
21
+
22
+ expect(@russian.context_by_keyword("date").keyword).to eq("date")
23
+ end
24
+ end
25
+
26
+ describe "matching tokens" do
27
+ it "should identify the right tokens" do
28
+ @context = Tr8n::LanguageContext.new(
29
+ :language => @english,
30
+ :keyword => "numeric",
31
+ :token_expression => '/.*(count|num|age|hours|minutes|years|seconds)(\d)*$/',
32
+ :variables => ['@n'],
33
+ :description => "Numeric language context"
34
+ )
35
+
36
+ expect(@context.token_expression).to eq(/.*(count|num|age|hours|minutes|years|seconds)(\d)*$/)
37
+ expect(@context.applies_to_token?("num")).to be_true
38
+ expect(@context.applies_to_token?("num1")).to be_true
39
+ expect(@context.applies_to_token?("profile_num1")).to be_true
40
+ expect(@context.applies_to_token?("profile_num21")).to be_true
41
+ expect(@context.applies_to_token?("profile")).to be_false
42
+ expect(@context.applies_to_token?("num_years")).to be_true
43
+ expect(@context.applies_to_token?("count1")).to be_true
44
+ expect(@context.applies_to_token?("count2")).to be_true
45
+ end
46
+ end
47
+
48
+ describe "gender rules" do
49
+ describe "loading variables" do
50
+ it "should return assigned variables" do
51
+ context = Tr8n::LanguageContext.new(
52
+ :language => @english,
53
+ :keyword => "gender",
54
+ :token_expression => '/.*(profile|user|actor|target)(\d)*$/',
55
+ :variables => ['@gender'],
56
+ :description => "Gender language context"
57
+ )
58
+
59
+ obj = double(:gender => "male")
60
+ Tr8n.config.stub(:context_rules) {
61
+ {
62
+ "gender" => {
63
+ "variables" => {
64
+ "@gender" => "gender",
65
+ }
66
+ }
67
+ }
68
+ }
69
+ expect(context.vars(obj)).to eq({"@gender" => "male"})
70
+
71
+ Tr8n.config.stub(:context_rules) {
72
+ {
73
+ :gender => {
74
+ :variables => {
75
+ "@gender" => "gender",
76
+ }
77
+ }
78
+ }
79
+ }
80
+ expect(context.vars(obj)).to eq({"@gender" => "male"})
81
+ expect(context.vars({:object => obj})).to eq({"@gender" => "male"})
82
+ expect(context.vars({:gender => "male"})).to eq({"@gender" => "male"})
83
+ expect(context.vars(:object => {:gender => "male"})).to eq({"@gender" => "male"})
84
+
85
+ Tr8n.config.stub(:context_rules) {
86
+ {
87
+ "gender" => {
88
+ "variables" => {
89
+ "@gender" => lambda{|obj| obj.gender},
90
+ }
91
+ }
92
+ }
93
+ }
94
+ expect(context.vars(obj)).to eq({"@gender" => "male"})
95
+ end
96
+ end
97
+
98
+ describe "evaluate rules" do
99
+ it "should return matching rule" do
100
+ context = Tr8n::LanguageContext.new(
101
+ :language => @english,
102
+ :keyword => "gender",
103
+ :token_expression => '/.*(profile|user|actor|target)(\d)*$/',
104
+ :variables => ['@gender'],
105
+ :description => "Gender language context",
106
+ :rules => {
107
+ :male => {:language_context => @context, :keyword => "male", :conditions => "(= 'male' @gender)"},
108
+ :female => {:language_context => @context, :keyword => "female", :conditions => "(= 'female' @gender)"},
109
+ :other => {:language_context => @context, :keyword => "other"}
110
+ }
111
+ )
112
+
113
+ Tr8n.config.stub(:context_rules) {
114
+ {
115
+ "gender" => {
116
+ "variables" => {
117
+ "@gender" => "gender",
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ expect(context.fallback_rule.keyword).to eq("other")
124
+
125
+ expect(context.find_matching_rule(double(:gender => "male")).keyword).to eq('male')
126
+ expect(context.find_matching_rule(double(:gender => "female")).keyword).to eq('female')
127
+ expect(context.find_matching_rule(double(:gender => "unknown")).keyword).to eq('other')
128
+
129
+ # unknown goes before other
130
+ context.rules[:unknown] = Tr8n::LanguageContextRule.new(:language_context => @context, :keyword => "unknown", :conditions => "(&& (!= 'male' @gender) (!= 'female' @gender))")
131
+ expect(context.find_matching_rule(double(:gender => "unknown")).keyword).to eq('unknown')
132
+ end
133
+ end
134
+ end
135
+
136
+ describe "genders rules" do
137
+ before(:each) do
138
+ @context = Tr8n::LanguageContext.new(
139
+ :language => @english,
140
+ :keyword => "genders",
141
+ :token_expression => '/.*(profiles|users|actors|targets)(\d)*$/',
142
+ :variables => ['@genders', '@count'],
143
+ :description => "Language context for list of users"
144
+ )
145
+
146
+ Tr8n.config.stub(:context_rules) {
147
+ {
148
+ "genders" => {
149
+ "variables" => {
150
+ "@genders" => lambda{|list|
151
+ list.collect{|u| u.gender}
152
+ },
153
+ "@count" => lambda{|list| list.count},
154
+ }
155
+ }
156
+ }
157
+ }
158
+ end
159
+
160
+ describe "loading variables" do
161
+ it "should return assigned variables" do
162
+ obj = [double(:gender => "male"), double(:gender => "female")]
163
+ expect(@context.vars(obj)).to eq({"@genders" => ["male", "female"], "@count" => 2})
164
+ end
165
+ end
166
+
167
+ describe "evaluate rules" do
168
+ it "should return matching rule" do
169
+ Tr8n.config.stub(:context_rules) {
170
+ {
171
+ "genders" => {
172
+ "variables" => {
173
+ "@genders" => lambda{|list| list.collect{|u| u.gender}},
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ @context.rules[:one_male] = Tr8n::LanguageContextRule.new(:conditions => "(&& (= 1 (count @genders)) (all @genders 'male'))")
180
+ @context.rules[:one_female] = Tr8n::LanguageContextRule.new(:conditions => "(&& (= 1 (count @genders)) (all @genders 'female'))")
181
+ @context.rules[:one_unknown] = Tr8n::LanguageContextRule.new(:conditions => "(&& (= 1 (count @genders)) (all @genders 'unknown'))")
182
+ @context.rules[:other] = Tr8n::LanguageContextRule.new(:keyword => "other")
183
+
184
+ expect(@context.find_matching_rule([double(:gender => "male")])).to eq(@context.rules[:one_male])
185
+ expect(@context.find_matching_rule([double(:gender => "female")])).to eq(@context.rules[:one_female])
186
+ expect(@context.find_matching_rule([double(:gender => "unknown")])).to eq(@context.rules[:one_unknown])
187
+ expect(@context.find_matching_rule([double(:gender => "male"), double(:gender => "male")])).to eq(@context.rules[:other])
188
+
189
+ # unknown goes before other
190
+ @context.rules[:at_least_two] = Tr8n::LanguageContextRule.new(:conditions => "(> (count @genders) 1)")
191
+ expect(@context.find_matching_rule([double(:gender => "male"), double(:gender => "male")])).to eq(@context.rules[:at_least_two])
192
+
193
+ @context.rules.delete(:at_least_two)
194
+
195
+ @context.rules[:all_male] = Tr8n::LanguageContextRule.new(:conditions => "(&& (> (count @genders) 1) (all @genders 'male'))")
196
+ @context.rules[:all_female] = Tr8n::LanguageContextRule.new(:conditions => "(&& (> (count @genders) 1) (all @genders 'female'))")
197
+
198
+ expect(@context.find_matching_rule([double(:gender => "male"), double(:gender => "male")])).to eq(@context.rules[:all_male])
199
+ expect(@context.find_matching_rule([double(:gender => "female"), double(:gender => "female")])).to eq(@context.rules[:all_female])
200
+ expect(@context.find_matching_rule([double(:gender => "male"), double(:gender => "female")])).to eq(@context.rules[:other])
201
+ end
202
+ end
203
+ end
204
+
205
+ describe "list rules" do
206
+ before(:each) do
207
+ @context = Tr8n::LanguageContext.new(
208
+ :language => @english,
209
+ :keyword => "list",
210
+ :token_expression => '/.*(list)(\d)*$/',
211
+ :variables => ['@count'],
212
+ :description => "Language context for lists"
213
+ )
214
+
215
+ Tr8n.config.stub(:context_rules) {
216
+ {
217
+ "list" => {
218
+ "variables" => {
219
+ "@count" => "count",
220
+ }
221
+ }
222
+ }
223
+ }
224
+ end
225
+
226
+ describe "loading variables" do
227
+ it "should return assigned variables" do
228
+ obj = ["apple", "banana"]
229
+ expect(@context.vars(obj)).to eq({"@count" => 2})
230
+ end
231
+ end
232
+
233
+ describe "evaluate rules" do
234
+ it "should return matching rule" do
235
+ @context.rules[:one] = Tr8n::LanguageContextRule.new(:conditions => "(= 1 @count)")
236
+ @context.rules[:two] = Tr8n::LanguageContextRule.new(:conditions => "(= 2 @count)")
237
+ @context.rules[:other] = Tr8n::LanguageContextRule.new(:keyword => "other")
238
+
239
+ expect(@context.find_matching_rule(["apple"])).to eq(@context.rules[:one])
240
+ expect(@context.find_matching_rule(["apple", "banana"])).to eq(@context.rules[:two])
241
+ expect(@context.find_matching_rule(["apple", "banana", "grapes"])).to eq(@context.rules[:other])
242
+ end
243
+ end
244
+ end
245
+
246
+ describe "numeric rules" do
247
+ before(:each) do
248
+ @context = Tr8n::LanguageContext.new(
249
+ :language => @english,
250
+ :keyword => "numeric",
251
+ :token_expression => '/.*(count|num|age|hours|minutes|years|seconds)(\d)*$/',
252
+ :variables => ['@n'],
253
+ :description => "Language context for numbers"
254
+ )
255
+
256
+ Tr8n.config.stub(:context_rules) {
257
+ {
258
+ "numeric" => {
259
+ "token_expression" => '/.*(count|num|age|hours|minutes|years|seconds)(\d)*$/',
260
+ "variables" => {
261
+ "@n" => "to_i",
262
+ }
263
+ }
264
+ }
265
+ }
266
+ end
267
+
268
+ describe "loading variables" do
269
+ it "should return assigned variables" do
270
+ obj = double(:to_i => 10)
271
+ expect(@context.vars(obj)).to eq({"@n" => 10})
272
+
273
+ obj = 15
274
+ expect(@context.vars(obj)).to eq({"@n" => 15})
275
+ end
276
+ end
277
+
278
+ describe "evaluate rules" do
279
+ it "should return matching rule" do
280
+ @context.rules[:one] = Tr8n::LanguageContextRule.new(:conditions => "(&& (= 1 (mod @n 10)) (!= 11 (mod @n 100)))", :description => "{n} mod 10 is 1 and {n} mod 100 is not 11", :examples => "1, 21, 31, 41, 51, 61...")
281
+ @context.rules[:few] = Tr8n::LanguageContextRule.new(:conditions => "(&& (in '2..4' (mod @n 10)) (not (in '12..14' (mod @n 100))))", :description => "{n} mod 10 in 2..4 and {n} mod 100 not in 12..14", :examples => "2-4, 22-24, 32-34...")
282
+ @context.rules[:many] = Tr8n::LanguageContextRule.new(:conditions => "(|| (= 0 (mod @n 10)) (in '5..9' (mod @n 10)) (in '11..14' (mod @n 100)))", :description => "{n} mod 10 is 0 or {n} mod 10 in 5..9 or {n} mod 100 in 11..14", :examples => "0, 5-20, 25-30, 35-40...")
283
+
284
+ expect(@context.find_matching_rule(1)).to eq(@context.rules[:one])
285
+ expect(@context.find_matching_rule(2)).to eq(@context.rules[:few])
286
+ expect(@context.find_matching_rule(0)).to eq(@context.rules[:many])
287
+ end
288
+ end
289
+ end
290
+
291
+ describe "date rules" do
292
+ before(:each) do
293
+ @context = Tr8n::LanguageContext.new(
294
+ :language => @english,
295
+ :keyword => "date",
296
+ :token_expression => '/.*(date)(\d)*$/',
297
+ :variables => ['@date'],
298
+ :description => "Language context for dates"
299
+ )
300
+
301
+ Tr8n.config.stub(:context_rules) {
302
+ {
303
+ "date" => {
304
+ "variables" => {
305
+ }
306
+ }
307
+ }
308
+ }
309
+ end
310
+
311
+ describe "loading variables" do
312
+ it "should return assigned variables" do
313
+ today = Date.today
314
+ expect(@context.vars(today)).to eq({"@date" => today})
315
+ end
316
+ end
317
+
318
+ describe "evaluate rules" do
319
+ it "should return matching rule" do
320
+ @context.rules[:present] = Tr8n::LanguageContextRule.new(:conditions => "(= (today) @date)")
321
+ @context.rules[:past] = Tr8n::LanguageContextRule.new(:conditions => "(> (today) @date)")
322
+ @context.rules[:future] = Tr8n::LanguageContextRule.new(:conditions => "(< (today) @date)")
323
+
324
+ expect(@context.find_matching_rule(Date.today)).to eq(@context.rules[:present])
325
+ expect(@context.find_matching_rule(Date.today - 24)).to eq(@context.rules[:past])
326
+ expect(@context.find_matching_rule(Date.today + 24)).to eq(@context.rules[:future])
327
+ end
328
+ end
329
+ end
330
+
331
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'helper'
4
+
5
+ describe Tr8n::Language do
6
+ describe "#initialize" do
7
+ it "sets language attributes" do
8
+ russian = Tr8n::Language.new(load_json('languages/ru.json'))
9
+ expect(russian.locale).to eq('ru')
10
+ expect(russian.full_name).to eq("Russian - Русский")
11
+ end
12
+ end
13
+
14
+
15
+
16
+ end
@@ -0,0 +1,148 @@
1
+ require File.expand_path('../helper', File.dirname(__FILE__))
2
+
3
+ describe Tr8n::RulesEngine::Evaluator do
4
+ describe "#evaluator" do
5
+ it "evaluates standard expressions" do
6
+ e = Tr8n::RulesEngine::Evaluator.new
7
+ e.evaluate(["label", "greeting", "hello world"])
8
+ expect(e.vars).to eq({"greeting"=>"hello world"})
9
+
10
+ expect(e.evaluate(["quote", [1,2,3]])).to eq([1,2,3])
11
+ expect(e.evaluate(["quote", ["a","b","c"]])).to eq(["a","b","c"])
12
+
13
+ expect(e.evaluate(["car", ["+", 1, 2]])).to eq(1)
14
+ expect(e.evaluate(["cdr", ["+", 1, 2]])).to eq([1, 2])
15
+ expect(e.evaluate(["cons", 1, ["quote", [2, 3]]])).to eq([1, 2, 3])
16
+
17
+ expect(e.evaluate(["eq", 1, 1])).to be_true
18
+
19
+ expect(e.evaluate(["atom", :a])).to be_true
20
+ expect(e.evaluate(["atom", "hello"])).to be_true
21
+ expect(e.evaluate(["atom", 1])).to be_true
22
+ expect(e.evaluate(["atom", 1.4])).to be_true
23
+ expect(e.evaluate(["atom", ["eq", 1, 1]])).to be_false
24
+
25
+ expect(e.evaluate(["cond", ["eq", 1, 1], 1, 0])).to eq(1)
26
+ expect(e.evaluate(["cond", ["eq", 1, 2], 1, 0])).to eq(0)
27
+ end
28
+
29
+ it "evaluates rules" do
30
+ e = Tr8n::RulesEngine::Evaluator.new
31
+ expect(e.evaluate(["=", "1", "1"])).to be_true
32
+ expect(e.evaluate(["=", "1", "2"])).to be_false
33
+
34
+ expect(e.evaluate(["!=", "2", "1"])).to be_true
35
+ expect(e.evaluate(["!=", "2", "2"])).to be_false
36
+
37
+ expect(e.evaluate([">", "2", "1"])).to be_true
38
+ expect(e.evaluate([">", "1", "2"])).to be_false
39
+
40
+ expect(e.evaluate(["<", "2", "3"])).to be_true
41
+ expect(e.evaluate(["<", "3", "2"])).to be_false
42
+
43
+ expect(e.evaluate(["+", 1, 2])).to eq(3)
44
+ expect(e.evaluate(["+", -1, 2])).to eq(1)
45
+
46
+ expect(e.evaluate(["-", 2, 1])).to eq(1)
47
+ expect(e.evaluate(["*", 2, 10])).to eq(20)
48
+ expect(e.evaluate(["/", 20, 10])).to eq(2)
49
+
50
+ expect(e.evaluate(["!", ["=", "1", "2"]])).to be_true
51
+
52
+ expect(e.evaluate(["&&", ["=", "1", "1"], ["=", 10, ["/", 20, 2]]])).to be_true
53
+ expect(e.evaluate(["||", ["=", "2", "1"], ["=", 10, ["/", 20, 2]]])).to be_true
54
+
55
+ expect(e.evaluate(["if", ["=", 1, 2], 1, 0])).to eq(0)
56
+
57
+ e.evaluate(["let", "@n", 1])
58
+ expect(e.vars).to eq({"@n"=>1})
59
+ expect(e.evaluate(["=", 1, "@n"])).to be_true
60
+
61
+ e.evaluate(["let", "@n", 11])
62
+ expect(e.vars).to eq({"@n"=>11})
63
+ expect(e.evaluate(["=", 11, "@n"])).to be_true
64
+
65
+ expect(e.evaluate(["and", ["=", "1", "1"], ["=", 10, ["/", 20, 2]]])).to be_true
66
+ expect(e.evaluate(["or", ["=", "2", "1"], ["=", 10, ["/", 20, 2]]])).to be_true
67
+
68
+ expect(e.evaluate(["mod", 23, 10])).to eq(3)
69
+ expect(e.evaluate(["mod", 2.3, 10])).to eq(2.3)
70
+
71
+ expect(e.evaluate(["match", "/hello/", "hello world"])).to be_true
72
+ expect(e.evaluate(["match", "hello", "hello world"])).to be_true
73
+ expect(e.evaluate(["match", "/^h/", "hello world"])).to be_true
74
+ expect(e.evaluate(["match", "/^e/", "hello world"])).to be_false
75
+ expect(e.evaluate(["match", "/^h.*d$/", "hello world"])).to be_true
76
+
77
+ expect(e.evaluate(["in", "1,2", "1"])).to be_true
78
+ expect(e.evaluate(["in", "1,2", 1])).to be_true
79
+ expect(e.evaluate(["in", "a,b,c", "a"])).to be_true
80
+ expect(e.evaluate(["in", "a..c, d..z", "h"])).to be_true
81
+
82
+ expect(e.evaluate(["in", "1..5, 10..22", 21])).to be_true
83
+ expect(e.evaluate(["in", "1..5, 10..22", 22])).to be_true
84
+ expect(e.evaluate(["in", "1..5, 10..22", 1])).to be_true
85
+ expect(e.evaluate(["in", "1..5, 10..22", 23])).to be_false
86
+ expect(e.evaluate(["in", "1..5, 10..22", "9"])).to be_false
87
+
88
+ expect(e.evaluate(["within", "1..3", 2.3])).to be_true
89
+ expect(e.evaluate(["within", "1..3", 3.3])).to be_false
90
+
91
+ expect(e.evaluate(["replace", "/^hello/", "hi", "hello world"])).to eq("hi world")
92
+ expect(e.evaluate(["replace", "world", "hi", "hello world"])).to eq("hello hi")
93
+ expect(e.evaluate(["replace", "o", "a", "hello world"])).to eq("hella warld")
94
+ expect(e.evaluate(["replace", "/world$/", "moon", "hello moon"])).to eq("hello moon")
95
+
96
+ expect(e.evaluate(["append", "world", "hello "])).to eq("hello world")
97
+ expect(e.evaluate(["prepend", "hello ", "world"])).to eq("hello world")
98
+
99
+ expect(e.evaluate(["true"])).to be_true
100
+ expect(e.evaluate(["false"])).to be_false
101
+
102
+ expect(e.evaluate(["date", "2011-01-01"])).to eq(Date.new(2011, 1, 1))
103
+ expect(e.evaluate(["today"])).to eq(Date.today)
104
+
105
+ expect(e.evaluate(["time", "2011-01-01 10:9:8"])).to eq(Time.new(2011, 1, 1, 10, 9, 8))
106
+
107
+ expect(e.evaluate([">", ["date", "3014-01-01"], ["today"]])).to be_true
108
+ end
109
+
110
+ it "evaluates expressions" do
111
+ e = Tr8n::RulesEngine::Evaluator.new
112
+
113
+ e.evaluate(["let", "@n", 1])
114
+ expect(e.evaluate(["&&", ["=", 1, ["mod", "@n", 10]], ["!=", 11, ["mod", "@n", 100]]])).to be_true
115
+ expect(e.evaluate(["&&", ["in", "2..4", ["mod", "@n", 10]], ["not", ["in", "12..14", ["mod", "@n", 100]]]])).to be_false
116
+ expect(e.evaluate(["||", ["=", 0, ["mod", "@n", 10]], ["in", "5..9", ["mod", "@n", 10]], ["in", "11..14", ["mod", "@n", 100]]])).to be_false
117
+
118
+ e.evaluate(["let", "@n", 21])
119
+ expect(e.evaluate(["&&", ["=", 1, ["mod", "@n", 10]], ["!=", 11, ["mod", "@n", 100]]])).to be_true
120
+ expect(e.evaluate(["&&", ["in", "2..4", ["mod", "@n", 10]], ["not", ["in", "12..14", ["mod", "@n", 100]]]])).to be_false
121
+ expect(e.evaluate(["||", ["=", 0, ["mod", "@n", 10]], ["in", "5..9", ["mod", "@n", 10]], ["in", "11..14", ["mod", "@n", 100]]])).to be_false
122
+
123
+ e.evaluate(["let", "@n", 11])
124
+ expect(e.evaluate(["&&", ["=", 1, ["mod", "@n", 10]], ["!=", 11, ["mod", "@n", 100]]])).to be_false
125
+ expect(e.evaluate(["&&", ["in", "2..4", ["mod", "@n", 10]], ["not", ["in", "12..14", ["mod", "@n", 100]]]])).to be_false
126
+ expect(e.evaluate(["||", ["=", 0, ["mod", "@n", 10]], ["in", "5..9", ["mod", "@n", 10]], ["in", "11..14", ["mod", "@n", 100]]])).to be_true
127
+
128
+ rules = {
129
+ "one" => ["&&", ["=", 1, ["mod", "@n", 10]], ["!=", 11, ["mod", "@n", 100]]],
130
+ "few" => ["&&", ["in", "2..4", ["mod", "@n", 10]], ["not", ["in", "12..14", ["mod", "@n", 100]]]],
131
+ "many" => ["||", ["=", 0, ["mod", "@n", 10]], ["in", "5..9", ["mod", "@n", 10]], ["in", "11..14", ["mod", "@n", 100]]]
132
+ }
133
+
134
+ {
135
+ "one" => [1, 21, 31, 41, 51, 61],
136
+ "few" => [2,3,4, 22,23,24, 32,33,34],
137
+ "many" => [0, 5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, 25,26,27,28,29,30, 35,36,37,38,39,40]
138
+ }.each do |key, values|
139
+ values.each do |value|
140
+ e.evaluate(["let", "@n", value])
141
+ expect(e.evaluate(rules[key])).to be_true
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path('../helper', File.dirname(__FILE__))
2
+
3
+ describe Tr8n::RulesEngine::Parser do
4
+ describe "#parser" do
5
+ it "parses expressions" do
6
+ expect(Tr8n::RulesEngine::Parser.new("(= 1 (mod n 10))").tokens).to eq(
7
+ ["(", "=", "1", "(", "mod", "n", "10", ")", ")"]
8
+ )
9
+
10
+ expect(Tr8n::RulesEngine::Parser.new("(&& (= 1 (mod @n 10)) (!= 11 (mod @n 100)))").tokens).to eq(
11
+ ["(", "&&", "(", "=", "1", "(", "mod", "@n", "10", ")", ")", "(", "!=", "11", "(", "mod", "@n", "100", ")", ")", ")"]
12
+ )
13
+
14
+ {
15
+ "(= 1 1)" => ["=", 1, 1],
16
+ "(+ 1 1)" => ["+", 1, 1],
17
+ "(= 1 (mod n 10))" => ["=", 1, ["mod", "n", 10]],
18
+ "(&& 1 1)" => ["&&", 1, 1],
19
+ "(mod @n 10)" => ["mod", "@n", 10],
20
+ "(&& (= 1 (mod @n 10)) (!= 11 (mod @n 100)))" => ["&&", ["=", 1, ["mod", "@n", 10]], ["!=", 11, ["mod", "@n", 100]]],
21
+ "(&& (in '2..4' (mod @n 10)) (not (in '12..14' (mod @n 100))))" => ["&&", ["in", "2..4", ["mod", "@n", 10]], ["not", ["in", "12..14", ["mod", "@n", 100]]]],
22
+ "(|| (= 0 (mod @n 10)) (in '5..9' (mod @n 10)) (in '11..14' (mod @n 100)))" => ["||", ["=", 0, ["mod", "@n", 10]], ["in", "5..9", ["mod", "@n", 10]], ["in", "11..14", ["mod", "@n", 100]]]
23
+ }.each do |source, target|
24
+ expect(Tr8n::RulesEngine::Parser.new(source).parse).to eq(target)
25
+ end
26
+
27
+ end
28
+ end
29
+ end