apes 1.0.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.
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