apes 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +82 -0
  4. data/.travis-gemfile +15 -0
  5. data/.travis.yml +15 -0
  6. data/.yardopts +1 -0
  7. data/CHANGELOG.md +3 -0
  8. data/Gemfile +22 -0
  9. data/README.md +177 -0
  10. data/Rakefile +44 -0
  11. data/apes.gemspec +34 -0
  12. data/doc/Apes.html +130 -0
  13. data/doc/Apes/Concerns.html +127 -0
  14. data/doc/Apes/Concerns/Errors.html +1089 -0
  15. data/doc/Apes/Concerns/Pagination.html +636 -0
  16. data/doc/Apes/Concerns/Request.html +766 -0
  17. data/doc/Apes/Concerns/Response.html +940 -0
  18. data/doc/Apes/Controller.html +1100 -0
  19. data/doc/Apes/Errors.html +125 -0
  20. data/doc/Apes/Errors/AuthenticationError.html +133 -0
  21. data/doc/Apes/Errors/BadRequestError.html +157 -0
  22. data/doc/Apes/Errors/BaseError.html +320 -0
  23. data/doc/Apes/Errors/InvalidDataError.html +157 -0
  24. data/doc/Apes/Errors/MissingDataError.html +157 -0
  25. data/doc/Apes/Model.html +378 -0
  26. data/doc/Apes/PaginationCursor.html +2138 -0
  27. data/doc/Apes/RuntimeConfiguration.html +909 -0
  28. data/doc/Apes/Serializers.html +125 -0
  29. data/doc/Apes/Serializers/JSON.html +389 -0
  30. data/doc/Apes/Serializers/JWT.html +452 -0
  31. data/doc/Apes/Serializers/List.html +347 -0
  32. data/doc/Apes/UrlsParser.html +1432 -0
  33. data/doc/Apes/Validators.html +125 -0
  34. data/doc/Apes/Validators/BaseValidator.html +278 -0
  35. data/doc/Apes/Validators/BooleanValidator.html +494 -0
  36. data/doc/Apes/Validators/EmailValidator.html +350 -0
  37. data/doc/Apes/Validators/PhoneValidator.html +375 -0
  38. data/doc/Apes/Validators/ReferenceValidator.html +372 -0
  39. data/doc/Apes/Validators/TimestampValidator.html +640 -0
  40. data/doc/Apes/Validators/UuidValidator.html +372 -0
  41. data/doc/Apes/Validators/ZipCodeValidator.html +372 -0
  42. data/doc/Apes/Version.html +189 -0
  43. data/doc/ApplicationController.html +547 -0
  44. data/doc/Concerns.html +128 -0
  45. data/doc/Concerns/ErrorHandling.html +826 -0
  46. data/doc/Concerns/PaginationHandling.html +463 -0
  47. data/doc/Concerns/RequestHandling.html +512 -0
  48. data/doc/Concerns/ResponseHandling.html +579 -0
  49. data/doc/Errors.html +126 -0
  50. data/doc/Errors/AuthenticationError.html +123 -0
  51. data/doc/Errors/BadRequestError.html +147 -0
  52. data/doc/Errors/BaseError.html +289 -0
  53. data/doc/Errors/InvalidDataError.html +147 -0
  54. data/doc/Errors/MissingDataError.html +147 -0
  55. data/doc/Model.html +315 -0
  56. data/doc/PaginationCursor.html +764 -0
  57. data/doc/Serializers.html +126 -0
  58. data/doc/Serializers/JSON.html +253 -0
  59. data/doc/Serializers/JWT.html +253 -0
  60. data/doc/Serializers/List.html +245 -0
  61. data/doc/Validators.html +126 -0
  62. data/doc/Validators/BaseValidator.html +209 -0
  63. data/doc/Validators/BooleanValidator.html +391 -0
  64. data/doc/Validators/EmailValidator.html +298 -0
  65. data/doc/Validators/PhoneValidator.html +313 -0
  66. data/doc/Validators/ReferenceValidator.html +284 -0
  67. data/doc/Validators/TimestampValidator.html +476 -0
  68. data/doc/Validators/UuidValidator.html +310 -0
  69. data/doc/Validators/ZipCodeValidator.html +310 -0
  70. data/doc/_index.html +435 -0
  71. data/doc/class_list.html +58 -0
  72. data/doc/css/common.css +1 -0
  73. data/doc/css/full_list.css +57 -0
  74. data/doc/css/style.css +339 -0
  75. data/doc/file.README.html +252 -0
  76. data/doc/file_list.html +60 -0
  77. data/doc/frames.html +26 -0
  78. data/doc/index.html +252 -0
  79. data/doc/js/app.js +219 -0
  80. data/doc/js/full_list.js +181 -0
  81. data/doc/js/jquery.js +4 -0
  82. data/doc/method_list.html +615 -0
  83. data/doc/top-level-namespace.html +112 -0
  84. data/lib/apes.rb +40 -0
  85. data/lib/apes/concerns/errors.rb +111 -0
  86. data/lib/apes/concerns/pagination.rb +81 -0
  87. data/lib/apes/concerns/request.rb +237 -0
  88. data/lib/apes/concerns/response.rb +74 -0
  89. data/lib/apes/controller.rb +77 -0
  90. data/lib/apes/errors.rb +38 -0
  91. data/lib/apes/model.rb +94 -0
  92. data/lib/apes/pagination_cursor.rb +152 -0
  93. data/lib/apes/runtime_configuration.rb +80 -0
  94. data/lib/apes/serializers.rb +88 -0
  95. data/lib/apes/urls_parser.rb +233 -0
  96. data/lib/apes/validators.rb +234 -0
  97. data/lib/apes/version.rb +24 -0
  98. data/spec/apes/concerns/errors_spec.rb +141 -0
  99. data/spec/apes/concerns/pagination_spec.rb +114 -0
  100. data/spec/apes/concerns/request_spec.rb +244 -0
  101. data/spec/apes/concerns/response_spec.rb +79 -0
  102. data/spec/apes/controller_spec.rb +54 -0
  103. data/spec/apes/errors_spec.rb +14 -0
  104. data/spec/apes/models_spec.rb +148 -0
  105. data/spec/apes/pagination_cursor_spec.rb +113 -0
  106. data/spec/apes/runtime_configuration_spec.rb +100 -0
  107. data/spec/apes/serializers_spec.rb +70 -0
  108. data/spec/apes/urls_parser_spec.rb +150 -0
  109. data/spec/apes/validators_spec.rb +237 -0
  110. data/spec/spec_helper.rb +30 -0
  111. data/views/_included.json.jbuilder +9 -0
  112. data/views/_pagination.json.jbuilder +9 -0
  113. data/views/collection.json.jbuilder +4 -0
  114. data/views/errors/400.json.jbuilder +9 -0
  115. data/views/errors/403.json.jbuilder +7 -0
  116. data/views/errors/404.json.jbuilder +6 -0
  117. data/views/errors/422.json.jbuilder +19 -0
  118. data/views/errors/500.json.jbuilder +12 -0
  119. data/views/errors/501.json.jbuilder +7 -0
  120. data/views/layouts/general.json.jbuilder +36 -0
  121. data/views/object.json.jbuilder +4 -0
  122. metadata +262 -0
@@ -0,0 +1,70 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
3
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
4
+ #
5
+
6
+ require "spec_helper"
7
+
8
+ describe Apes::Serializers::List do
9
+ describe ".load" do
10
+ it "split a string" do
11
+ expect(Apes::Serializers::List.load(", 1,2 4,,3,A , B ")).to eq(["1", "2 4", "3", "A", "B"])
12
+ end
13
+
14
+ it "always returns a array" do
15
+ expect(Apes::Serializers::List.load(nil)).to eq([])
16
+ end
17
+ end
18
+
19
+ describe ".dump" do
20
+ it "encodes everything as an array" do
21
+ expect(Apes::Serializers::List.dump([])).to eq("")
22
+ expect(Apes::Serializers::List.dump([1, "2", 3])).to eq("1,2,3")
23
+ expect(Apes::Serializers::List.dump(1)).to eq("1")
24
+ end
25
+ end
26
+ end
27
+
28
+ describe Apes::Serializers::JSON do
29
+ describe ".load" do
30
+ it "load a JSON object" do
31
+ expect(Apes::Serializers::JSON.load("{\"1\": 2}")).to eq({"1" => 2})
32
+ end
33
+
34
+ it "an Hash is returned with indifferent access" do
35
+ expect(Apes::Serializers::JSON.load("{\"1\": 2}")).to be_a(HashWithIndifferentAccess)
36
+ end
37
+
38
+ it "fallbacks to default for invalid JSON" do
39
+ expect(Apes::Serializers::JSON.load("\"", false, "A")).to eq("A")
40
+ end
41
+ end
42
+
43
+ describe ".dump" do
44
+ it "encodes input as JSON" do
45
+ expect(Apes::Serializers::JSON.dump({a: "B"})).to eq("{\"a\":\"B\"}")
46
+ end
47
+ end
48
+ end
49
+
50
+ describe Apes::Serializers::JWT do
51
+ describe ".load" do
52
+ it "load a JSON object" do
53
+ expect(Apes::Serializers::JWT.load("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYXRhIiwic3ViIjp7IjEiOjJ9fQ.jqhJuB0xtO7Xyk_T8X_rwOKE96Q7GBxWR5NBxfW5xWE")).to eq({"1" => 2})
54
+ end
55
+
56
+ it "an Hash is returned with indifferent access" do
57
+ expect(Apes::Serializers::JWT.load("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYXRhIiwic3ViIjp7IjEiOjJ9fQ.jqhJuB0xtO7Xyk_T8X_rwOKE96Q7GBxWR5NBxfW5xWE")).to be_a(HashWithIndifferentAccess)
58
+ end
59
+
60
+ it "fallbacks to default for invalid JSON" do
61
+ expect(Apes::Serializers::JWT.load("\"", false, "A")).to eq("A")
62
+ end
63
+ end
64
+
65
+ describe ".dump" do
66
+ it "encodes input as JSON" do
67
+ expect(Apes::Serializers::JWT.dump({a: "B"})).to eq("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYXRhIiwic3ViIjp7ImEiOiJCIn19.mWyWy9SDzOlBLJW1b4ICw4QZwoGDU836ED5EALZnjeU")
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,150 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
3
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
4
+ #
5
+
6
+ require "spec_helper"
7
+
8
+ describe Apes::UrlsParser do
9
+ describe ".instance" do
10
+ it "should return a singleton" do
11
+ first = Apes::UrlsParser.instance
12
+ expect(Apes::UrlsParser.instance).to be(first)
13
+ end
14
+
15
+ it "should force recreation of a new singleton" do
16
+ original = Apes::UrlsParser.instance
17
+ expect(Apes::UrlsParser.instance).to be(original)
18
+ expect(Apes::UrlsParser.instance(true)).not_to be(original)
19
+ end
20
+ end
21
+
22
+ describe "#url?" do
23
+ it "should recognize whether a text is a url" do
24
+ expect(Apes::UrlsParser.instance.url?("http://google.it")).to be_truthy
25
+ expect(Apes::UrlsParser.instance.url?("google.co")).to be_truthy
26
+ expect(Apes::UrlsParser.instance.url?("fab.foog.google.co")).to be_truthy
27
+ expect(Apes::UrlsParser.instance.url?("google.co:123/abc?query=a&utc=b#whatever")).to be_truthy
28
+ expect(Apes::UrlsParser.instance.url?("google.ca#123")).to be_truthy
29
+ expect(Apes::UrlsParser.instance.url?("google.c")).to be_falsey
30
+ expect(Apes::UrlsParser.instance.url?("http://google.cowtech")).to be_falsey
31
+ expect(Apes::UrlsParser.instance.url?("http://google.it::123")).to be_falsey
32
+ expect(Apes::UrlsParser.instance.url?("fab..foog.google.co")).to be_falsey
33
+ expect(Apes::UrlsParser.instance.url?("test@google.com")).to be_falsey
34
+ expect(Apes::UrlsParser.instance.url?("http://127.0.0.1")).to be_truthy
35
+ expect(Apes::UrlsParser.instance.url?("127.0.0.1")).to be_truthy
36
+ expect(Apes::UrlsParser.instance.url?("http://[1762:0:0:0:0:B03:1:AF18]")).to be_truthy
37
+ expect(Apes::UrlsParser.instance.url?("[1762::B03:1:AF18]")).to be_truthy
38
+ expect(Apes::UrlsParser.instance.url?("[::127.0.0.1]")).to be_truthy
39
+ end
40
+ end
41
+
42
+ describe "#email?" do
43
+ it "should recognize whether a text is a email" do
44
+ expect(Apes::UrlsParser.instance.email?("abc@google.it")).to be_truthy
45
+ expect(Apes::UrlsParser.instance.email?("abc.cde@google.co")).to be_truthy
46
+ expect(Apes::UrlsParser.instance.email?("ab'c.cde@google.co")).to be_truthy
47
+ expect(Apes::UrlsParser.instance.email?("abc-af_123@fab.foog.google.co")).to be_truthy
48
+ expect(Apes::UrlsParser.instance.email?("ad$1@local.com")).to be_falsey
49
+ end
50
+ end
51
+
52
+ describe "#domain?" do
53
+ it "should recognize whether a text is a domain" do
54
+ expect(Apes::UrlsParser.instance.domain?("google.it")).to be_truthy
55
+ expect(Apes::UrlsParser.instance.domain?("google.co")).to be_truthy
56
+ expect(Apes::UrlsParser.instance.domain?("fab.foog.google.co")).to be_truthy
57
+ expect(Apes::UrlsParser.instance.domain?("google.co:123/abc?query=a&utc=b#whatever")).to be_falsey
58
+ expect(Apes::UrlsParser.instance.domain?("google.c")).to be_falsey
59
+ expect(Apes::UrlsParser.instance.domain?("google.it:123")).to be_falsey
60
+ end
61
+ end
62
+
63
+ describe "#shortened?" do
64
+ it "should recognize whether a URL is a shortened one" do
65
+ expect(Apes::UrlsParser.instance.shortened?("https://bit.ly/ABC")).to be_truthy
66
+ expect(Apes::UrlsParser.instance.shortened?("bit.ly/ABC")).to be_truthy
67
+ expect(Apes::UrlsParser.instance.shortened?("bit.ly/ACC")).to be_truthy
68
+ expect(Apes::UrlsParser.instance.shortened?("cow.tc/ACC")).to be_falsey
69
+ expect(Apes::UrlsParser.instance.shortened?("cow.tc/ACC", "cow.tc")).to be_truthy
70
+ expect(Apes::UrlsParser.instance.shortened?("abit.ly/ACC")).to be_falsey
71
+ end
72
+ end
73
+
74
+ describe "#extract_urls" do
75
+ it "should extract URLS from a text" do
76
+ text = "
77
+ This is a pretty complex text. It has a URL without a domain which is google.it, also it contains a separator and now let's also put two
78
+ overlapping URLs http://cowtech.it and http://cowtech.it/en and, finally, a shortened one http://bit.ly/1000 and one using custom domain
79
+ http://cnn.it/1GFkZQs and email test@gmail.com. Fun, isn't it?
80
+ "
81
+
82
+ expect(Apes::UrlsParser.instance.extract_urls(text)).to eq(["google.it", "http://cowtech.it", "http://cowtech.it/en", "http://bit.ly/1000", "http://cnn.it/1GFkZQs"])
83
+ expect(Apes::UrlsParser.instance.extract_urls(text, sort: :asc)).to eq(["google.it", "http://cowtech.it", "http://bit.ly/1000", "http://cowtech.it/en", "http://cnn.it/1GFkZQs"])
84
+ expect(Apes::UrlsParser.instance.extract_urls(text, sort: :desc)).to eq(["http://cnn.it/1GFkZQs", "http://cowtech.it/en", "http://bit.ly/1000", "http://cowtech.it", "google.it"])
85
+ expect(Apes::UrlsParser.instance.extract_urls(text, mode: :shortened)).to eq(["http://bit.ly/1000"])
86
+ expect(Apes::UrlsParser.instance.extract_urls(text, mode: :unshortened)).to eq(["google.it", "http://cowtech.it", "http://cowtech.it/en", "http://cnn.it/1GFkZQs"])
87
+ expect(Apes::UrlsParser.instance.extract_urls(text, mode: :shortened, shortened_domains: ["cnn.it"])).to eq(["http://bit.ly/1000", "http://cnn.it/1GFkZQs"])
88
+ expect(Apes::UrlsParser.instance.extract_urls(text, mode: :unshortened, shortened_domains: ["cnn.it"])).to eq(["google.it", "http://cowtech.it", "http://cowtech.it/en"])
89
+ end
90
+ end
91
+
92
+ describe "#replace_urls" do
93
+ it "should replace URLS in a text" do
94
+ text = "
95
+ This is a pretty complex text. It has a URL without a domain which is google.it, also it contains a separator and now let's also put two
96
+ overlapping URLs http://cowtech.it and http://cowtech.it/en and, finally, a shortened one http://bit.ly/1000 and one using custom domain
97
+ http://cnn.it/1GFkZQs. Fun, isn't it?
98
+ "
99
+
100
+ replacements = {
101
+ "http://cowtech.it" => "A",
102
+ "http://cowtech.it/en" => "B",
103
+ "http://bit.ly/1000" => "C",
104
+ "http://cnn.it/1GFkZQs" => "D"
105
+ }
106
+
107
+ expect(Apes::UrlsParser.instance.replace_urls(text, replacements: replacements)).to eq("
108
+ This is a pretty complex text. It has a URL without a domain which is google.it, also it contains a separator and now let's also put two
109
+ overlapping URLs http://A and http://B and, finally, a shortened one http://C and one using custom domain
110
+ http://D. Fun, isn't it?
111
+ ")
112
+
113
+ expect(Apes::UrlsParser.instance.replace_urls(text, replacements: replacements, mode: :unshortened)).to eq("
114
+ This is a pretty complex text. It has a URL without a domain which is google.it, also it contains a separator and now let's also put two
115
+ overlapping URLs http://A and http://B and, finally, a shortened one http://bit.ly/1000 and one using custom domain
116
+ http://D. Fun, isn't it?
117
+ ")
118
+
119
+ expect(Apes::UrlsParser.instance.replace_urls(text, replacements: replacements, mode: :shortened)).to eq("
120
+ This is a pretty complex text. It has a URL without a domain which is google.it, also it contains a separator and now let's also put two
121
+ overlapping URLs http://cowtech.it and http://cowtech.it/en and, finally, a shortened one http://C and one using custom domain
122
+ http://cnn.it/1GFkZQs. Fun, isn't it?
123
+ ")
124
+
125
+ expect(Apes::UrlsParser.instance.replace_urls(text, replacements: replacements, mode: :shortened, shortened_domains: ["cnn.it"])).to eq("
126
+ This is a pretty complex text. It has a URL without a domain which is google.it, also it contains a separator and now let's also put two
127
+ overlapping URLs http://cowtech.it and http://cowtech.it/en and, finally, a shortened one http://C and one using custom domain
128
+ http://D. Fun, isn't it?
129
+ ")
130
+ end
131
+ end
132
+
133
+ describe "#clean" do
134
+ it "should remove separators after a url" do
135
+ expect(Apes::UrlsParser.instance.clean(" http://google.it ")).to eq("http://google.it")
136
+ expect(Apes::UrlsParser.instance.clean(" http://google.it,")).to eq("http://google.it")
137
+ expect(Apes::UrlsParser.instance.clean(" http://google.it.")).to eq("http://google.it")
138
+ expect(Apes::UrlsParser.instance.clean(" http://google.it:")).to eq("http://google.it")
139
+ end
140
+ end
141
+
142
+ describe "#hashify" do
143
+ it "should generate a hash out of a URL" do
144
+ expect(Apes::UrlsParser.instance.hashify("google.it")).to eq("dae16e97dcaa41bdace5765826a28a4192b7f2ec9d96993d158ef0413fd3cab5")
145
+ expect(Apes::UrlsParser.instance.hashify("http://google.it")).to eq("dae16e97dcaa41bdace5765826a28a4192b7f2ec9d96993d158ef0413fd3cab5")
146
+ expect(Apes::UrlsParser.instance.hashify("bit.ly/1000")).to eq("1de660471e79f834336526379fd76bebbcb69057f36237bdd82f8af58d9641de")
147
+ expect(Apes::UrlsParser.instance.hashify("http://bit.ly/1000")).to eq("1de660471e79f834336526379fd76bebbcb69057f36237bdd82f8af58d9641de")
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,237 @@
1
+ #
2
+ # This file is part of the apes gem. Copyright (C) 2016 and above Shogun <shogun@cowtech.it>.
3
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
4
+ #
5
+
6
+ require "spec_helper"
7
+
8
+ describe Apes::Validators::BaseValidator do
9
+ class BaseMockValidationValidator < Apes::Validators::BaseValidator
10
+ def check_valid?(_)
11
+ false
12
+ end
13
+ end
14
+
15
+ class BaseMockModel
16
+ include ActiveModel::Validations
17
+ include Apes::Model
18
+
19
+ attr_reader :field, :other_field
20
+ validates :field, "base_mock_validation" => {message: "ERROR"}
21
+ validates :other_field, "base_mock_validation" => {default_message: "DEFAULT", additional: true}
22
+ end
23
+
24
+ describe "#validate_each" do
25
+ it "should correctly record messages" do
26
+ subject = BaseMockModel.new
27
+ subject.validate
28
+ expect(subject.errors.to_hash).to eq({field: ["ERROR"]})
29
+ expect(subject.additional_errors.to_hash).to eq({other_field: ["DEFAULT"]})
30
+ end
31
+ end
32
+ end
33
+
34
+ describe Apes::Validators::UuidValidator do
35
+ class UUIDMockModel
36
+ include ActiveModel::Validations
37
+ include Apes::Model
38
+
39
+ attr_accessor :field
40
+ validates :field, "apes/validators/uuid" => true
41
+ end
42
+
43
+ describe "#validate_each" do
44
+ it "should correctly validate fields" do
45
+ subject = UUIDMockModel.new
46
+
47
+ subject.field = "d250e78f-887a-4da2-8b4f-59f61c809bed"
48
+ subject.validate
49
+ expect(subject.errors.to_hash).to eq({})
50
+
51
+ subject.field = "100"
52
+ subject.validate
53
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid UUID"]})
54
+ end
55
+ end
56
+ end
57
+
58
+ describe Apes::Validators::ReferenceValidator do
59
+ class ReferenceMockOtherModel
60
+ SECONDARY_QUERY = "name = :id"
61
+ end
62
+
63
+ class ReferenceMockModel
64
+ include ActiveModel::Validations
65
+ include Apes::Model
66
+
67
+ attr_accessor :field
68
+ validates :field, "apes/validators/reference" => {class_name: "reference_mock_other_model"}
69
+ end
70
+
71
+ describe "#validate_each" do
72
+ before(:each) do
73
+ allow(ReferenceMockOtherModel).to receive(:find_with_any).and_return(false)
74
+ end
75
+
76
+ it "should correctly validate fields" do
77
+ subject = ReferenceMockModel.new
78
+ subject.field = 100
79
+ subject.validate
80
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid ReferenceMockOtherModel (cannot find a ReferenceMockOtherModel with id \"100\")"]})
81
+ end
82
+
83
+ it "should correctly validate fields when they are array" do
84
+ subject = ReferenceMockModel.new
85
+ subject.field = [100, 101]
86
+ subject.validate
87
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid ReferenceMockOtherModel (cannot find a ReferenceMockOtherModel with id \"100\")", "must be a valid ReferenceMockOtherModel (cannot find a ReferenceMockOtherModel with id \"101\")"]})
88
+ end
89
+ end
90
+ end
91
+
92
+ describe Apes::Validators::EmailValidator do
93
+ class EmailMockModel
94
+ include ActiveModel::Validations
95
+ include Apes::Model
96
+
97
+ attr_accessor :field
98
+ validates :field, "apes/validators/email" => true
99
+ end
100
+
101
+ describe "#validate_each" do
102
+ it "should correctly validate fields" do
103
+ subject = EmailMockModel.new
104
+ subject.field = 100
105
+ subject.validate
106
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid email"]})
107
+ end
108
+ end
109
+ end
110
+
111
+ describe Apes::Validators::BooleanValidator do
112
+ class BooleanMockModel
113
+ include ActiveModel::Validations
114
+ include Apes::Model
115
+
116
+ attr_accessor :field
117
+ validates :field, "apes/validators/boolean" => true
118
+ end
119
+
120
+ describe ".parse" do
121
+ it "should correctly parse a value" do
122
+ expect(Apes::Validators::BooleanValidator.parse("YES")).to be_truthy
123
+ expect(Apes::Validators::BooleanValidator.parse("OTHER")).to be_falsey
124
+ end
125
+
126
+ it "should raise errors if asked to" do
127
+ expect(Apes::Validators::BooleanValidator.parse(nil, raise_errors: true)).to be_falsey
128
+ expect { Apes::Validators::BooleanValidator.parse("", raise_errors: true) }.to raise_error(ArgumentError, "Invalid boolean value \"\".")
129
+ expect { Apes::Validators::BooleanValidator.parse("FOO", raise_errors: true) }.to raise_error(ArgumentError, "Invalid boolean value \"FOO\".")
130
+ end
131
+ end
132
+
133
+ describe "#validate_each" do
134
+ it "should correctly validate fields" do
135
+ subject = BooleanMockModel.new
136
+ subject.field = 100
137
+ subject.validate
138
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid truthy/falsey value"]})
139
+ end
140
+ end
141
+ end
142
+
143
+ describe Apes::Validators::PhoneValidator do
144
+ class PhoneMockModel
145
+ include ActiveModel::Validations
146
+ include Apes::Model
147
+
148
+ attr_accessor :field
149
+ validates :field, "apes/validators/phone" => true
150
+ end
151
+
152
+ describe "#validate_each" do
153
+ it "should correctly validate fields" do
154
+ subject = PhoneMockModel.new
155
+
156
+ subject.field = "+1 650-762-4637"
157
+ subject.validate
158
+ expect(subject.errors.to_hash).to eq({})
159
+
160
+ subject.field = "FOO"
161
+ subject.validate
162
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid phone"]})
163
+ end
164
+ end
165
+ end
166
+
167
+ describe Apes::Validators::ZipCodeValidator do
168
+ class ZipCodeMockModel
169
+ include ActiveModel::Validations
170
+ include Apes::Model
171
+
172
+ attr_accessor :field
173
+ validates :field, "apes/validators/zip_code" => true
174
+ end
175
+
176
+ describe "#validate_each" do
177
+ it "should correctly validate fields" do
178
+ subject = ZipCodeMockModel.new
179
+
180
+ subject.field = "12345"
181
+ subject.validate
182
+ expect(subject.errors.to_hash).to eq({})
183
+
184
+ subject.field = "12345-6789"
185
+ subject.validate
186
+ expect(subject.errors.to_hash).to eq({})
187
+
188
+
189
+ subject.field = "100"
190
+ subject.validate
191
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid ZIP code"]})
192
+ end
193
+ end
194
+ end
195
+
196
+ describe Apes::Validators::TimestampValidator do
197
+ class TimestampMockModel
198
+ include ActiveModel::Validations
199
+ include Apes::Model
200
+
201
+ attr_accessor :field, :other_field
202
+ validates :field, "apes/validators/timestamp" => true
203
+ validates :other_field, "apes/validators/timestamp" => {formats: ["%Y"]}
204
+ end
205
+
206
+ describe ".parse" do
207
+ it "should parse respecting formats, using ISO-8601 formats by default" do
208
+ ISO8601 = "%FT%T%z".freeze
209
+ FULL_ISO8601 = "%FT%T.%L%z".freeze
210
+
211
+ allow(Apes::RuntimeConfiguration).to receive(:timestamp_formats).and_return({default: "%FT%T%z", full: "%FT%T.%L%z", clean: "%Y%m%dT%H%M%S.%L%z", clean_full: "%Y%m%dT%H%M%S.%L%z"})
212
+ expect(Apes::Validators::TimestampValidator.parse("2016-05-04T03:02:01+06:00")).to eq(DateTime.civil(2016, 5, 4, 3, 2, 1, "+6"))
213
+ expect(Apes::Validators::TimestampValidator.parse("2016-05-04T03:02:01.789-05:00")).to eq(DateTime.civil(2016, 5, 4, 3, 2, 1.789, "-5"))
214
+ expect(Apes::Validators::TimestampValidator.parse("2016-05-04")).to be_nil
215
+ expect(Apes::Validators::TimestampValidator.parse("2016-05-04+06:00", formats: ["%F%z"])).to eq(DateTime.civil(2016, 5, 4, 0, 0, 0, "+6"))
216
+ end
217
+
218
+ it "should raise errors if asked to" do
219
+ expect { Apes::Validators::TimestampValidator.parse("2016-05-04", raise_errors: true) } .to raise_error(ArgumentError, "Invalid timestamp \"2016-05-04\".")
220
+ end
221
+ end
222
+
223
+ describe "#validate_each" do
224
+ it "should correctly validate fields" do
225
+ subject = TimestampMockModel.new
226
+ subject.field = 2016
227
+ subject.other_field = "xxxx"
228
+ subject.validate
229
+ expect(subject.errors.to_hash).to eq({field: ["must be a valid ISO 8601 timestamp"], other_field: ["must be a valid ISO 8601 timestamp"]})
230
+
231
+ subject.field = Time.now
232
+ subject.other_field = "2016"
233
+ subject.validate
234
+ expect(subject.errors.to_hash).to eq({})
235
+ end
236
+ end
237
+ end