hoodoo 1.0.2

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 (216) hide show
  1. checksums.yaml +7 -0
  2. data/bin/hoodoo +5 -0
  3. data/lib/hoodoo.rb +27 -0
  4. data/lib/hoodoo/active.rb +32 -0
  5. data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
  6. data/lib/hoodoo/active/active_record/base.rb +81 -0
  7. data/lib/hoodoo/active/active_record/creator.rb +134 -0
  8. data/lib/hoodoo/active/active_record/dated.rb +343 -0
  9. data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
  10. data/lib/hoodoo/active/active_record/finder.rb +606 -0
  11. data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
  12. data/lib/hoodoo/active/active_record/secure.rb +431 -0
  13. data/lib/hoodoo/active/active_record/support.rb +106 -0
  14. data/lib/hoodoo/active/active_record/translated.rb +87 -0
  15. data/lib/hoodoo/active/active_record/uuid.rb +80 -0
  16. data/lib/hoodoo/active/active_record/writer.rb +321 -0
  17. data/lib/hoodoo/client.rb +23 -0
  18. data/lib/hoodoo/client/augmented_array.rb +29 -0
  19. data/lib/hoodoo/client/augmented_base.rb +168 -0
  20. data/lib/hoodoo/client/augmented_hash.rb +23 -0
  21. data/lib/hoodoo/client/client.rb +354 -0
  22. data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
  23. data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
  24. data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
  25. data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
  26. data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
  27. data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
  28. data/lib/hoodoo/client/headers.rb +269 -0
  29. data/lib/hoodoo/communicators.rb +23 -0
  30. data/lib/hoodoo/communicators/fast.rb +44 -0
  31. data/lib/hoodoo/communicators/pool.rb +601 -0
  32. data/lib/hoodoo/communicators/slow.rb +84 -0
  33. data/lib/hoodoo/data.rb +51 -0
  34. data/lib/hoodoo/data/resources/caller.rb +39 -0
  35. data/lib/hoodoo/data/resources/errors.rb +28 -0
  36. data/lib/hoodoo/data/resources/log.rb +31 -0
  37. data/lib/hoodoo/data/resources/session.rb +26 -0
  38. data/lib/hoodoo/data/types/error_primitive.rb +27 -0
  39. data/lib/hoodoo/data/types/permissions.rb +40 -0
  40. data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
  41. data/lib/hoodoo/data/types/permissions_full.rb +28 -0
  42. data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
  43. data/lib/hoodoo/discovery.rb +20 -0
  44. data/lib/hoodoo/errors.rb +19 -0
  45. data/lib/hoodoo/errors/error_descriptions.rb +229 -0
  46. data/lib/hoodoo/errors/errors.rb +322 -0
  47. data/lib/hoodoo/generator.rb +139 -0
  48. data/lib/hoodoo/logger.rb +23 -0
  49. data/lib/hoodoo/logger/fast_writer.rb +27 -0
  50. data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
  51. data/lib/hoodoo/logger/logger.rb +387 -0
  52. data/lib/hoodoo/logger/slow_writer.rb +49 -0
  53. data/lib/hoodoo/logger/writer_mixin.rb +52 -0
  54. data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
  55. data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
  56. data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
  57. data/lib/hoodoo/middleware.rb +33 -0
  58. data/lib/hoodoo/presenters.rb +45 -0
  59. data/lib/hoodoo/presenters/base.rb +281 -0
  60. data/lib/hoodoo/presenters/base_dsl.rb +519 -0
  61. data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
  62. data/lib/hoodoo/presenters/embedding.rb +232 -0
  63. data/lib/hoodoo/presenters/types/array.rb +118 -0
  64. data/lib/hoodoo/presenters/types/boolean.rb +26 -0
  65. data/lib/hoodoo/presenters/types/date.rb +26 -0
  66. data/lib/hoodoo/presenters/types/date_time.rb +26 -0
  67. data/lib/hoodoo/presenters/types/decimal.rb +47 -0
  68. data/lib/hoodoo/presenters/types/enum.rb +55 -0
  69. data/lib/hoodoo/presenters/types/field.rb +158 -0
  70. data/lib/hoodoo/presenters/types/float.rb +26 -0
  71. data/lib/hoodoo/presenters/types/hash.rb +361 -0
  72. data/lib/hoodoo/presenters/types/integer.rb +26 -0
  73. data/lib/hoodoo/presenters/types/object.rb +117 -0
  74. data/lib/hoodoo/presenters/types/string.rb +53 -0
  75. data/lib/hoodoo/presenters/types/tags.rb +24 -0
  76. data/lib/hoodoo/presenters/types/text.rb +26 -0
  77. data/lib/hoodoo/presenters/types/uuid.rb +54 -0
  78. data/lib/hoodoo/services.rb +34 -0
  79. data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
  80. data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
  81. data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
  82. data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
  83. data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
  84. data/lib/hoodoo/services/discovery/discovery.rb +186 -0
  85. data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
  86. data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
  87. data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
  88. data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
  89. data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
  90. data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
  91. data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
  92. data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
  93. data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
  94. data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
  95. data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
  96. data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
  97. data/lib/hoodoo/services/middleware/interaction.rb +127 -0
  98. data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
  99. data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
  100. data/lib/hoodoo/services/services/context.rb +153 -0
  101. data/lib/hoodoo/services/services/implementation.rb +132 -0
  102. data/lib/hoodoo/services/services/interface.rb +934 -0
  103. data/lib/hoodoo/services/services/permissions.rb +250 -0
  104. data/lib/hoodoo/services/services/request.rb +189 -0
  105. data/lib/hoodoo/services/services/response.rb +316 -0
  106. data/lib/hoodoo/services/services/service.rb +141 -0
  107. data/lib/hoodoo/services/services/session.rb +729 -0
  108. data/lib/hoodoo/utilities.rb +12 -0
  109. data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
  110. data/lib/hoodoo/utilities/utilities.rb +380 -0
  111. data/lib/hoodoo/utilities/uuid.rb +44 -0
  112. data/lib/hoodoo/version.rb +17 -0
  113. data/spec/active/active_record/base_spec.rb +57 -0
  114. data/spec/active/active_record/creator_spec.rb +88 -0
  115. data/spec/active/active_record/dated_spec.rb +248 -0
  116. data/spec/active/active_record/error_mapping_spec.rb +360 -0
  117. data/spec/active/active_record/finder_spec.rb +744 -0
  118. data/spec/active/active_record/search_helper_spec.rb +384 -0
  119. data/spec/active/active_record/secure_spec.rb +435 -0
  120. data/spec/active/active_record/support_spec.rb +225 -0
  121. data/spec/active/active_record/translated_spec.rb +19 -0
  122. data/spec/active/active_record/uuid_spec.rb +72 -0
  123. data/spec/active/active_record/writer_spec.rb +272 -0
  124. data/spec/alchemy/alchemy-amq.rb +33 -0
  125. data/spec/client/augmented_array_spec.rb +15 -0
  126. data/spec/client/augmented_base_spec.rb +50 -0
  127. data/spec/client/augmented_hash_spec.rb +15 -0
  128. data/spec/client/client_spec.rb +955 -0
  129. data/spec/client/endpoint/endpoint_spec.rb +70 -0
  130. data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
  131. data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
  132. data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
  133. data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
  134. data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
  135. data/spec/client/headers_spec.rb +172 -0
  136. data/spec/communicators/fast_spec.rb +9 -0
  137. data/spec/communicators/pool_spec.rb +339 -0
  138. data/spec/communicators/slow_spec.rb +15 -0
  139. data/spec/data/resources/caller_spec.rb +156 -0
  140. data/spec/data/resources/errors_spec.rb +22 -0
  141. data/spec/data/resources/log_spec.rb +20 -0
  142. data/spec/data/resources/session_spec.rb +15 -0
  143. data/spec/data/types/error_primitive_spec.rb +15 -0
  144. data/spec/data/types/permissions_defaults_spec.rb +25 -0
  145. data/spec/data/types/permissions_full_spec.rb +44 -0
  146. data/spec/data/types/permissions_resources_spec.rb +34 -0
  147. data/spec/data/types/permissions_spec.rb +37 -0
  148. data/spec/errors/error_descriptions_spec.rb +98 -0
  149. data/spec/errors/errors_spec.rb +346 -0
  150. data/spec/integration/service_actions_spec.rb +112 -0
  151. data/spec/logger/fast_writer_spec.rb +18 -0
  152. data/spec/logger/logger_spec.rb +259 -0
  153. data/spec/logger/slow_writer_spec.rb +144 -0
  154. data/spec/logger/writers/file_writer_spec.rb +37 -0
  155. data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
  156. data/spec/logger/writers/stream_writer_spec.rb +38 -0
  157. data/spec/presenters/base_dsl_spec.rb +111 -0
  158. data/spec/presenters/base_spec.rb +871 -0
  159. data/spec/presenters/common_resource_fields_spec.rb +30 -0
  160. data/spec/presenters/embedding_spec.rb +87 -0
  161. data/spec/presenters/types/array_spec.rb +249 -0
  162. data/spec/presenters/types/boolean_spec.rb +51 -0
  163. data/spec/presenters/types/date_spec.rb +57 -0
  164. data/spec/presenters/types/date_time_spec.rb +59 -0
  165. data/spec/presenters/types/decimal_spec.rb +58 -0
  166. data/spec/presenters/types/enum_spec.rb +71 -0
  167. data/spec/presenters/types/field_spec.rb +77 -0
  168. data/spec/presenters/types/float_spec.rb +50 -0
  169. data/spec/presenters/types/hash_spec.rb +1069 -0
  170. data/spec/presenters/types/integer_spec.rb +50 -0
  171. data/spec/presenters/types/object_spec.rb +177 -0
  172. data/spec/presenters/types/string_spec.rb +65 -0
  173. data/spec/presenters/types/tags_spec.rb +56 -0
  174. data/spec/presenters/types/text_spec.rb +50 -0
  175. data/spec/presenters/types/uuid_spec.rb +46 -0
  176. data/spec/presenters/walk_spec.rb +198 -0
  177. data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
  178. data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
  179. data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
  180. data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
  181. data/spec/services/discovery/discovery_spec.rb +73 -0
  182. data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
  183. data/spec/services/discovery/results/for_http_spec.rb +37 -0
  184. data/spec/services/discovery/results/for_local_spec.rb +21 -0
  185. data/spec/services/discovery/results/for_remote_spec.rb +15 -0
  186. data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
  187. data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
  188. data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
  189. data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
  190. data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
  191. data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
  192. data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
  193. data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
  194. data/spec/services/middleware/middleware_cors_spec.rb +93 -0
  195. data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
  196. data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
  197. data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
  198. data/spec/services/middleware/middleware_logging_spec.rb +356 -0
  199. data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
  200. data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
  201. data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
  202. data/spec/services/middleware/middleware_public_spec.rb +238 -0
  203. data/spec/services/middleware/middleware_spec.rb +1569 -0
  204. data/spec/services/middleware/string_inquirer_spec.rb +30 -0
  205. data/spec/services/services/application_spec.rb +74 -0
  206. data/spec/services/services/context_spec.rb +48 -0
  207. data/spec/services/services/implementation_spec.rb +45 -0
  208. data/spec/services/services/interface_spec.rb +262 -0
  209. data/spec/services/services/permissions_spec.rb +249 -0
  210. data/spec/services/services/request_spec.rb +95 -0
  211. data/spec/services/services/response_spec.rb +250 -0
  212. data/spec/services/services/session_spec.rb +432 -0
  213. data/spec/spec_helper.rb +298 -0
  214. data/spec/utilities/utilities_spec.rb +537 -0
  215. data/spec/utilities/uuid_spec.rb +20 -0
  216. metadata +615 -0
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Presenters::DateTime do
4
+
5
+ before do
6
+ @inst = Hoodoo::Presenters::DateTime.new('one',:required => false)
7
+ end
8
+
9
+ describe '#validate' do
10
+ it 'should return [] when valid datetime' do
11
+ expect(@inst.validate('2014-12-11T00:00:00Z').errors).to eq([])
12
+ expect(@inst.validate('2014-12-11T00:00:00.0Z').errors).to eq([])
13
+ expect(@inst.validate('2014-12-11T00:00:00.0000Z').errors).to eq([])
14
+ expect(@inst.validate('2014-12-11T00:00:00.00000000Z').errors).to eq([])
15
+ expect(@inst.validate('2014-12-11T00:00:00+12:30').errors).to eq([])
16
+ expect(@inst.validate('2014-12-11T00:00:00-12:30').errors).to eq([])
17
+ expect(@inst.validate('2014-12-11T00:00:00.0+12:30').errors).to eq([])
18
+ expect(@inst.validate('2014-12-11T00:00:00.0-12:30').errors).to eq([])
19
+ end
20
+
21
+ it 'should not return error when not required and absent' do
22
+ expect(@inst.validate(nil).errors).to eq([])
23
+ end
24
+
25
+ it 'should return error when required and absent' do
26
+ @inst.required = true
27
+ expect(@inst.validate(nil).errors).to eq([
28
+ {'code'=>"generic.required_field_missing", 'message'=>"Field `one` is required", 'reference'=>"one"}
29
+ ])
30
+ end
31
+
32
+ it 'should return correct error when datetime is invalid' do
33
+ errors = @inst.validate('2014-99-99T00:00:00Z')
34
+
35
+ err = [ {'code'=>"generic.invalid_datetime", 'message'=>"Field `one` is an invalid ISO8601 datetime", 'reference'=>"one"}]
36
+ expect(errors.errors).to eq(err)
37
+ end
38
+
39
+ it 'should return correct error with non datetime types' do
40
+ err = [ {'code'=>"generic.invalid_datetime", 'message'=>"Field `one` is an invalid ISO8601 datetime", 'reference'=>"one"}]
41
+
42
+ expect(@inst.validate('asckn').errors).to eq(err)
43
+ expect(@inst.validate('2014-12-11').errors).to eq(err)
44
+ expect(@inst.validate(34534.234).errors).to eq(err)
45
+ expect(@inst.validate(38247).errors).to eq(err)
46
+ expect(@inst.validate(true).errors).to eq(err)
47
+ expect(@inst.validate({}).errors).to eq(err)
48
+ expect(@inst.validate([]).errors).to eq(err)
49
+ end
50
+
51
+ it 'should return correct error with path' do
52
+ errors = @inst.validate('scdacs','ordinary')
53
+ expect(errors.errors).to eq([
54
+ {'code'=>"generic.invalid_datetime", 'message'=>"Field `ordinary.one` is an invalid ISO8601 datetime", 'reference'=>"ordinary.one"}
55
+ ])
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Presenters::Decimal do
4
+
5
+ before do
6
+ @inst = Hoodoo::Presenters::Decimal.new('one',:precision => 20)
7
+ end
8
+
9
+ describe '#initialize' do
10
+ it 'should raise an error if precision is not defined' do
11
+ expect {
12
+ Hoodoo::Presenters::Decimal.new('one',:required => false)
13
+ }.to raise_error(ArgumentError)
14
+ end
15
+ end
16
+
17
+ describe '#validate' do
18
+ it 'should return [] when valid decimal' do
19
+ expect(@inst.validate(BigDecimal.new(12.231,3)).errors).to eq([])
20
+ end
21
+
22
+ it 'should return correct error when data is not a decimal' do
23
+ errors = @inst.validate('asckn')
24
+
25
+ err = [ {'code'=>"generic.invalid_decimal", 'message'=>"Field `one` is an invalid decimal", 'reference'=>"one"}]
26
+ expect(errors.errors).to eq(err)
27
+ end
28
+
29
+ it 'should not return error when not required and absent' do
30
+ expect(@inst.validate(nil).errors).to eq([])
31
+ end
32
+
33
+ it 'should return error when required and absent' do
34
+ @inst.required = true
35
+ expect(@inst.validate(nil).errors).to eq([
36
+ {'code'=>"generic.required_field_missing", 'message'=>"Field `one` is required", 'reference'=>"one"}
37
+ ])
38
+ end
39
+
40
+ it 'should return correct error with non decimal types' do
41
+ err = [ {'code'=>"generic.invalid_decimal", 'message'=>"Field `one` is an invalid decimal", 'reference'=>"one"}]
42
+
43
+ expect(@inst.validate('asckn').errors).to eq(err)
44
+ expect(@inst.validate(34534).errors).to eq(err)
45
+ expect(@inst.validate(true).errors).to eq(err)
46
+ expect(@inst.validate({}).errors).to eq(err)
47
+ expect(@inst.validate([]).errors).to eq(err)
48
+ end
49
+
50
+ it 'should return correct error with path' do
51
+ errors = @inst.validate('scdacs','ordinary')
52
+ expect(errors.errors).to eq([
53
+ {'code'=>"generic.invalid_decimal", 'message'=>"Field `ordinary.one` is an invalid decimal", 'reference'=>"ordinary.one"}
54
+ ])
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Presenters::Enum do
4
+
5
+ before do
6
+ class TestPresenter4 < Hoodoo::Presenters::Base
7
+
8
+ schema do
9
+ array :an_array, :required => true do
10
+ integer :an_integer
11
+ datetime :a_datetime
12
+ end
13
+ enum :an_enum, :from => [ :one, 'two', 3 ]
14
+ text :some_text
15
+ end
16
+
17
+ end
18
+ end
19
+
20
+ describe '::schema' do
21
+ it 'should raise an error if we use :from incorrectly' do
22
+ expect {
23
+ class ErroneousEnumTest < Hoodoo::Presenters::Base
24
+ schema do
25
+ enum :from => "wrong!"
26
+ end
27
+ end
28
+ }.to raise_error(ArgumentError)
29
+ end
30
+ end
31
+
32
+ describe '#validate' do
33
+ it 'should insist on string values' do
34
+ data = {
35
+ :an_array => [],
36
+ :an_enum => :one
37
+ }
38
+
39
+ data = Hoodoo::Utilities.stringify(data)
40
+ errors = TestPresenter4.validate(data)
41
+ expect(errors.errors).to eq([
42
+ {'code'=>"generic.invalid_enum", 'message'=>"Field `an_enum` does not contain an allowed reference value from this list: `[\"one\", \"two\", \"3\"]`", 'reference'=>"an_enum"}
43
+ ])
44
+ end
45
+
46
+ it 'should ensure only an enumerated value is given' do
47
+ data = {
48
+ :an_array => [],
49
+ :an_enum => 'hello'
50
+ }
51
+
52
+ data = Hoodoo::Utilities.stringify(data)
53
+ errors = TestPresenter4.validate(data)
54
+ expect(errors.errors).to eq([
55
+ {'code'=>"generic.invalid_enum", 'message'=>"Field `an_enum` does not contain an allowed reference value from this list: `[\"one\", \"two\", \"3\"]`", 'reference'=>"an_enum"}
56
+ ])
57
+ end
58
+
59
+ it 'should be happy with valid values' do
60
+ data = {
61
+ :an_array => [],
62
+ :an_enum => '3'
63
+ }
64
+
65
+ data = Hoodoo::Utilities.stringify(data)
66
+ errors = TestPresenter4.validate(data)
67
+ expect(errors.errors).to eq([])
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Presenters::Field do
4
+
5
+ before do
6
+ @inst = Hoodoo::Presenters::Field.new('one',:required => false)
7
+ end
8
+
9
+ describe '#initialise' do
10
+
11
+ it 'should initialize correctly' do
12
+ inst = Hoodoo::Presenters::Field.new('one',:required => true)
13
+ expect(inst.name).to eq('one')
14
+ expect(inst.required).to eq(true)
15
+ end
16
+
17
+ it 'should default required false' do
18
+ inst = Hoodoo::Presenters::Field.new('two')
19
+ expect(inst.name).to eq('two')
20
+ expect(inst.required).to eq(false)
21
+ end
22
+ end
23
+
24
+ describe '#validate' do
25
+ it 'should return [] when not required when data nil' do
26
+ errors = @inst.validate(nil)
27
+ expect(errors.errors).to eq([])
28
+ end
29
+
30
+ it 'should return [] when not required when data not nil' do
31
+ errors = @inst.validate(1)
32
+ expect(errors.errors).to eq([])
33
+ end
34
+
35
+ it 'should return correct error when required and data is nil' do
36
+ @inst.required = true
37
+ errors = @inst.validate(nil)
38
+ expect(errors.errors).to eq([
39
+ {'code'=>"generic.required_field_missing", 'message'=>"Field `one` is required", 'reference'=>"one"}
40
+ ])
41
+ end
42
+
43
+ it 'should return correct error with path' do
44
+ @inst.required = true
45
+ errors = @inst.validate(nil,'ordinary')
46
+ expect(errors.errors).to eq([
47
+ {'code'=>"generic.required_field_missing", 'message'=>"Field `ordinary.one` is required", 'reference'=>"ordinary.one"}
48
+ ])
49
+ end
50
+ end
51
+
52
+ describe '#full_path' do
53
+
54
+ it 'should return name.to_s if no path' do
55
+ expect(@inst.full_path(nil)).to eq(@inst.name)
56
+ end
57
+
58
+ it 'should return name.to_s if path empty' do
59
+ expect(@inst.full_path('')).to eq(@inst.name)
60
+ end
61
+
62
+ it 'should return paths if name nil' do
63
+ @inst.name = nil
64
+ expect(@inst.full_path('sdzcz')).to eq('sdzcz')
65
+ end
66
+
67
+ it 'should return paths if name empty' do
68
+ @inst.name = ''
69
+ expect(@inst.full_path('sdzcz')).to eq('sdzcz')
70
+ end
71
+
72
+ it 'should return path.name if path and name not nil or empty' do
73
+ expect(@inst.full_path('sdzcz')).to eq('sdzcz.one')
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Presenters::Float do
4
+
5
+ before do
6
+ @inst = Hoodoo::Presenters::Float.new('one',:required => false)
7
+ end
8
+
9
+ describe '#validate' do
10
+ it 'should return [] when valid float' do
11
+ expect(@inst.validate(12.231).errors).to eq([])
12
+ end
13
+
14
+ it 'should return correct error when data is not a float' do
15
+ errors = @inst.validate('asckn')
16
+
17
+ err = [ {'code'=>"generic.invalid_float", 'message'=>"Field `one` is an invalid float", 'reference'=>"one"}]
18
+ expect(errors.errors).to eq(err)
19
+ end
20
+
21
+ it 'should not return error when not required and absent' do
22
+ expect(@inst.validate(nil).errors).to eq([])
23
+ end
24
+
25
+ it 'should return error when required and absent' do
26
+ @inst.required = true
27
+ expect(@inst.validate(nil).errors).to eq([
28
+ {'code'=>"generic.required_field_missing", 'message'=>"Field `one` is required", 'reference'=>"one"}
29
+ ])
30
+ end
31
+
32
+ it 'should return correct error with non float types' do
33
+ err = [ {'code'=>"generic.invalid_float", 'message'=>"Field `one` is an invalid float", 'reference'=>"one"}]
34
+
35
+ expect(@inst.validate('asckn').errors).to eq(err)
36
+ expect(@inst.validate(34534).errors).to eq(err)
37
+ expect(@inst.validate(true).errors).to eq(err)
38
+ expect(@inst.validate({}).errors).to eq(err)
39
+ expect(@inst.validate([]).errors).to eq(err)
40
+ end
41
+
42
+ it 'should return correct error with path' do
43
+ errors = @inst.validate('scdacs','ordinary')
44
+ expect(errors.errors).to eq([
45
+ {'code'=>"generic.invalid_float", 'message'=>"Field `ordinary.one` is an invalid float", 'reference'=>"ordinary.one"}
46
+ ])
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,1069 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Presenters::Hash do
4
+
5
+ context 'exceptions' do
6
+ it 'should complain about #key then #keys' do
7
+ expect {
8
+ class TestHashKeyKeysException < Hoodoo::Presenters::Base
9
+ schema do
10
+ hash :foo do
11
+ key :one
12
+ keys :length => 4
13
+ end
14
+ end
15
+ end
16
+ }.to raise_error( RuntimeError )
17
+ end
18
+
19
+ it 'should complain about #keys then #key' do
20
+ expect {
21
+ class TestHashKeysKeyException < Hoodoo::Presenters::Base
22
+ schema do
23
+ hash :foo do
24
+ keys :length => 4
25
+ key :one
26
+ end
27
+ end
28
+ end
29
+ }.to raise_error( RuntimeError )
30
+ end
31
+
32
+ it 'should complain about #keys twice' do
33
+ expect {
34
+ class TestHashKeysKeysException < Hoodoo::Presenters::Base
35
+ schema do
36
+ hash :foo do
37
+ keys :length => 4
38
+ keys :length => 4
39
+ end
40
+ end
41
+ end
42
+ }.to raise_error( RuntimeError )
43
+ end
44
+ end
45
+
46
+ ############################################################################
47
+
48
+ class TestHashNoKeysPresenter < Hoodoo::Presenters::Base
49
+ schema do
50
+ hash :specific
51
+ end
52
+ end
53
+
54
+ class TestHashNoKeysPresenterRequired < Hoodoo::Presenters::Base
55
+ schema do
56
+ hash :specific_required, :required => true
57
+ end
58
+ end
59
+
60
+ ############################################################################
61
+
62
+ context 'no keys' do
63
+ context '#validate' do
64
+ it 'should return [] when valid object' do
65
+ data = { 'specific' => { 'hello' => 'there' } }
66
+ expect( TestHashNoKeysPresenter.validate( data ).errors ).to eq( [] )
67
+ end
68
+
69
+ it 'should return correct error when data is not an object' do
70
+ data = { 'specific' => 'hello' }
71
+ errors = [ {
72
+ 'code' => 'generic.invalid_hash',
73
+ 'message' => 'Field `specific` is an invalid hash',
74
+ 'reference' => 'specific'
75
+ } ]
76
+
77
+ expect( TestHashNoKeysPresenter.validate( data ).errors ).to eq( errors )
78
+ end
79
+
80
+ it 'should not return error when not required and absent' do
81
+ data = { 'foo' => 'bar' }
82
+ expect( TestHashNoKeysPresenter.validate( data ).errors ).to eq( [] )
83
+ end
84
+
85
+ it 'should return error when required and absent' do
86
+ data = { 'foo' => 'bar' }
87
+ errors = [ {
88
+ 'code' => 'generic.required_field_missing',
89
+ 'message' => 'Field `specific_required` is required',
90
+ 'reference' => 'specific_required'
91
+ } ]
92
+
93
+ expect( TestHashNoKeysPresenterRequired.validate( data ).errors ).to eq( errors )
94
+ end
95
+ end
96
+
97
+ context '#render' do
98
+ it 'should render correctly' do
99
+ data = { 'specific' => { 'hello' => 'there' } }
100
+ result = TestHashNoKeysPresenter.render( data )
101
+
102
+ expect( result ).to eq( data )
103
+ end
104
+
105
+ it 'should render correctly when described hash is not required and absent' do
106
+ data = { 'foo' => 'bar' }
107
+ result = TestHashNoKeysPresenter.render( data )
108
+
109
+ expect( result ).to eq({})
110
+ end
111
+ end
112
+ end
113
+
114
+ ############################################################################
115
+
116
+ class TestHashSpecificKeyPresenter < Hoodoo::Presenters::Base
117
+ schema do
118
+ hash :specific do
119
+ key :one
120
+ key :two do
121
+ string :foo, :length => 10
122
+ text :bar, :required => true
123
+ end
124
+ key :three do
125
+ integer :int
126
+ uuid :id
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ ############################################################################
133
+
134
+ context 'specific keys' do
135
+ context '#validate' do
136
+ it 'should return [] when valid object (1)' do
137
+ data = { 'specific' => { 'one' => 'anything' } }
138
+ expect( TestHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
139
+ end
140
+
141
+ it 'should return [] when valid object (2)' do
142
+ data = { 'specific' => { 'two' => { 'foo' => 'foov', 'bar' => 'barv' } } }
143
+ expect( TestHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
144
+ end
145
+
146
+ it 'should return [] when valid object (3)' do
147
+ data = { 'specific' => { 'three' => { 'int' => 23, 'id' => Hoodoo::UUID.generate() } } }
148
+ expect( TestHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
149
+ end
150
+
151
+ it 'should return [] when valid object (4)' do
152
+ data = { 'specific' => { 'one' => 'anything',
153
+ 'two' => { 'foo' => 'foov', 'bar' => 'barv' },
154
+ 'three' => { 'int' => 23, 'id' => Hoodoo::UUID.generate() } } }
155
+
156
+ expect( TestHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
157
+ end
158
+
159
+ it 'should return correct error when data is not an object' do
160
+ data = { 'specific' => 'hello' }
161
+ errors = [ {
162
+ 'code' => 'generic.invalid_hash',
163
+ 'message' => 'Field `specific` is an invalid hash',
164
+ 'reference' => 'specific'
165
+ } ]
166
+
167
+ expect( TestHashSpecificKeyPresenter.validate( data ).errors ).to eq( errors )
168
+ end
169
+
170
+ it 'should return correct error when unexpected keys are present' do
171
+ data = { 'specific' => { 'hi' => 'there',
172
+ 'foo' => { 'bar' => 'baz' },
173
+ 'three' => { 'int' => 23, 'id' => Hoodoo::UUID.generate() } } }
174
+ errors = [ {
175
+ 'code' => 'generic.invalid_hash',
176
+ 'message' => 'Field `specific` is an invalid hash due to unrecognised keys `hi, foo`',
177
+ 'reference' => 'specific'
178
+ } ]
179
+
180
+ expect( TestHashSpecificKeyPresenter.validate( data ).errors ).to eq( errors )
181
+ end
182
+
183
+ it 'should return correct errors when value formats are wrong or required values are omitted' do
184
+ data = { 'specific' => { 'three' => { 'int' => 'not an int', 'id' => 'not an id' }, 'two' => {} } }
185
+
186
+ errors = [
187
+ {
188
+ 'code' => 'generic.invalid_integer',
189
+ 'message' => 'Field `specific.three.int` is an invalid integer',
190
+ 'reference' => 'specific.three.int'
191
+ },
192
+ {
193
+ 'code' => 'generic.invalid_uuid',
194
+ 'message' => 'Field `specific.three.id` is an invalid UUID',
195
+ 'reference' => 'specific.three.id'
196
+ },
197
+ {
198
+ 'code' => 'generic.required_field_missing',
199
+ 'message' => 'Field `specific.two.bar` is required',
200
+ 'reference' => 'specific.two.bar'
201
+ },
202
+ ]
203
+
204
+ expect( TestHashSpecificKeyPresenter.validate( data ).errors ).to eq( errors )
205
+ end
206
+ end
207
+
208
+ context '#render' do
209
+ it 'should render correctly (1)' do
210
+ data = { 'specific' => { 'one' => 'anything' } }
211
+ result = TestHashSpecificKeyPresenter.render( data )
212
+
213
+ expect( result ).to eq( data )
214
+ end
215
+
216
+ it 'should render correctly (2)' do
217
+ data = { 'specific' => { 'two' => { 'foo' => 'foov', 'bar' => 'barv' } } }
218
+ result = TestHashSpecificKeyPresenter.render( data )
219
+
220
+ expect( result ).to eq( data )
221
+ end
222
+
223
+ it 'should render correctly (3)' do
224
+ data = { 'specific' => { 'three' => { 'int' => 23, 'id' => Hoodoo::UUID.generate() } } }
225
+ result = TestHashSpecificKeyPresenter.render( data )
226
+
227
+ expect( result ).to eq( data )
228
+ end
229
+
230
+ it 'should render correctly (4)' do
231
+ data = { 'specific' => { 'one' => 'anything',
232
+ 'two' => { 'foo' => 'foov', 'bar' => 'barv' },
233
+ 'three' => { 'int' => 23, 'id' => Hoodoo::UUID.generate() } } }
234
+ result = TestHashSpecificKeyPresenter.render( data )
235
+
236
+ expect( result ).to eq( data )
237
+ end
238
+
239
+ it 'should ignore unspecified entries' do
240
+ inner = { 'one' => 'anything',
241
+ 'two' => { 'foo' => 'foov', 'bar' => 'barv' },
242
+ 'three' => { 'int' => 23, 'id' => Hoodoo::UUID.generate() } }
243
+
244
+ valid = { 'specific' => inner.dup }
245
+ data = { 'specific' => inner.dup }
246
+
247
+ data[ 'generic' ] = 'hello'
248
+ data[ 'specific' ][ 'random' ] = 23
249
+
250
+ result = TestHashSpecificKeyPresenter.render( data )
251
+
252
+ expect( result ).to eq( valid )
253
+ end
254
+ end
255
+ end
256
+
257
+ ############################################################################
258
+
259
+ class TestNestedHashSpecificKeyPresenter < Hoodoo::Presenters::Base
260
+ schema do
261
+ object :obj do
262
+ text :obj_text
263
+ hash :specific do
264
+ key :two do
265
+ string :two_key_string, :length => 10
266
+ hash :two_key_hash do
267
+ key :inner
268
+ key :inner_2 do
269
+ string :inner_2_string, :length => 4
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+
278
+ ############################################################################
279
+
280
+ context 'nested specific keys' do
281
+ context '#validate' do
282
+ it 'should return [] when valid object (1)' do
283
+ data = { 'obj' => { 'specific' => {} } }
284
+ expect( TestNestedHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
285
+ end
286
+
287
+ it 'should return [] when valid object (2)' do
288
+ data = { 'obj' => { 'obj_text' => 'hello', 'specific' => { 'two' => {} } } }
289
+ expect( TestNestedHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
290
+ end
291
+
292
+ it 'should return error with incorrect nested keys (1)' do
293
+ data = { 'obj' => { 'obj_text' => 'hello', 'specific' => { 'three' => {} } } }
294
+ errors = [
295
+ {
296
+ 'code' => 'generic.invalid_hash',
297
+ 'message' => 'Field `obj.specific` is an invalid hash due to unrecognised keys `three`',
298
+ 'reference' => 'obj.specific'
299
+ }
300
+ ]
301
+
302
+ expect( TestNestedHashSpecificKeyPresenter.validate( data ).errors ).to eq( errors )
303
+ end
304
+
305
+ it 'should return [] when valid object (3)' do
306
+ data = { 'obj' => { 'obj_text' => 'hello', 'specific' => { 'two' => { 'two_key_hash' => {} } } } }
307
+ expect( TestNestedHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
308
+ end
309
+
310
+ it 'should return [] when valid object (4)' do
311
+ data = { 'obj' => { 'obj_text' => 'hello', 'specific' => { 'two' => { 'two_key_hash' => { 'inner' => true } } } } }
312
+ expect( TestNestedHashSpecificKeyPresenter.validate( data ).errors ).to eq( [] )
313
+ end
314
+
315
+ it 'should return error with incorrect nested keys (2)' do
316
+ data = { 'obj' => { 'obj_text' => 'hello', 'specific' => { 'two' => { 'two_key_hash' => { 'madeup' => true } } } } }
317
+ errors = [
318
+ {
319
+ 'code' => 'generic.invalid_hash',
320
+ 'message' => 'Field `obj.specific.two.two_key_hash` is an invalid hash due to unrecognised keys `madeup`',
321
+ 'reference' => 'obj.specific.two.two_key_hash'
322
+ }
323
+ ]
324
+
325
+ expect( TestNestedHashSpecificKeyPresenter.validate( data ).errors ).to eq( errors )
326
+ end
327
+
328
+ it 'should return error with incorrect nested values' do
329
+ data = { 'obj' => { 'obj_text' => 'hello', 'specific' => { 'two' => { 'two_key_hash' => { 'inner_2' => { 'inner_2_string' => 'too-long-for-here' } } } } } }
330
+ errors = [
331
+ {
332
+ 'code' => 'generic.invalid_string',
333
+ 'message' => 'Field `obj.specific.two.two_key_hash.inner_2.inner_2_string` is longer than maximum length `4`',
334
+ 'reference' => 'obj.specific.two.two_key_hash.inner_2.inner_2_string'
335
+ }
336
+ ]
337
+
338
+ expect( TestNestedHashSpecificKeyPresenter.validate( data ).errors ).to eq( errors )
339
+ end
340
+ end
341
+
342
+ context '#render' do
343
+ it 'should render complex entity correctly' do
344
+ valid = { 'obj' => { 'obj_text' => 'hello', 'specific' => { 'two' => { 'two_key_hash' => { 'inner' => 42, 'inner_2' => { 'inner_2_string' => 'ok' } } } } } }
345
+ data = { 'obj' => { 'obj_text' => 'hello', 'random' => true, 'specific' => { 'two' => { 'two_key_hash' => { 'inner' => 42, 'inner_2' => { 'inner_2_string' => 'ok' } } } } } }
346
+
347
+ result = TestNestedHashSpecificKeyPresenter.render( data )
348
+ expect( result ).to eq( valid )
349
+ end
350
+ end
351
+ end
352
+
353
+ ############################################################################
354
+
355
+ class TestHashSpecificKeyPresenterWithRequirements < Hoodoo::Presenters::Base
356
+ schema do
357
+ hash :specific do
358
+ key :one, :required => true
359
+ key :two, :required => true do
360
+ string :foo, :length => 10
361
+ text :bar, :required => true
362
+ end
363
+ end
364
+ end
365
+ end
366
+
367
+ ############################################################################
368
+
369
+ context 'specific keys with requirements' do
370
+ context '#validate' do
371
+ it 'should return correct errors when keys are omitted (1)' do
372
+ data = { 'specific' => {} }
373
+ errors = [
374
+ {
375
+ 'code' => 'generic.required_field_missing',
376
+ 'message' => 'Field `specific.one` is required',
377
+ 'reference' => 'specific.one'
378
+ },
379
+ {
380
+ 'code' => 'generic.required_field_missing',
381
+ 'message' => 'Field `specific.two` is required',
382
+ 'reference' => 'specific.two'
383
+ },
384
+ ]
385
+
386
+ expect( TestHashSpecificKeyPresenterWithRequirements.validate( data ).errors ).to eq( errors )
387
+ end
388
+
389
+ it 'should return correct errors when keys are omitted (2)' do
390
+ data = { 'specific' => { 'two' => { 'foo' => 'foov' } } }
391
+ errors = [
392
+ {
393
+ 'code' => 'generic.required_field_missing',
394
+ 'message' => 'Field `specific.two.bar` is required',
395
+ 'reference' => 'specific.two.bar'
396
+ },
397
+ {
398
+ 'code' => 'generic.required_field_missing',
399
+ 'message' => 'Field `specific.one` is required',
400
+ 'reference' => 'specific.one'
401
+ }
402
+ ]
403
+
404
+ expect( TestHashSpecificKeyPresenterWithRequirements.validate( data ).errors ).to eq( errors )
405
+ end
406
+ end
407
+ end
408
+
409
+ ############################################################################
410
+
411
+ class TestHashSpecificKeyPresenterWithDefaults < Hoodoo::Presenters::Base
412
+ schema do
413
+ hash :specific_defaults, :default => { 'one' => 'anything', 'two' => { 'foo' => 'valid' }, 'ignoreme' => 'invalid' } do
414
+ key :one, :default => { 'foo' => { 'bar' => 'baz' } }
415
+ key :two do
416
+ string :foo, :length => 10
417
+ text :bar, :default => 'this is the text field for "bar"'
418
+ integer :baz, :default => 42
419
+ end
420
+ key :three, :default => { 'bar' => 'for_key_three' } do
421
+ string :foo, :length => 10
422
+ text :bar, :default => 'this is the text field for "bar"'
423
+ integer :baz, :default => 42
424
+ end
425
+ end
426
+ end
427
+ end
428
+
429
+ class TestHashSpecificKeyPresenterWithDefaultsExceptHash < Hoodoo::Presenters::Base
430
+ schema do
431
+ hash :specific_defaults do
432
+ key :one, :default => { 'foo' => { 'bar' => 'baz' } }
433
+ key :two do
434
+ string :foo, :length => 10
435
+ text :bar, :default => 'this is the text field for "bar"'
436
+ integer :baz, :default => 42
437
+ end
438
+ end
439
+ end
440
+ end
441
+
442
+ ############################################################################
443
+
444
+ context 'specific keys with defaults' do
445
+ context '#render' do
446
+
447
+ # The hash itself has a default set, so if the hash key is omitted we
448
+ # expect to get the canned default hash instead.
449
+ #
450
+ it 'should render with correct default for whole hash (1)' do
451
+ result = TestHashSpecificKeyPresenterWithDefaults.render( {} )
452
+
453
+ expect( result ).to eq({
454
+ 'specific_defaults' => {
455
+ 'one' => 'anything',
456
+ 'two' => { 'foo' => 'valid', 'bar' => 'this is the text field for "bar"', 'baz' => 42 },
457
+ 'three' => { 'bar' => 'for_key_three', 'baz' => 42 }
458
+ }
459
+ })
460
+ end
461
+
462
+ # Should behave the same with 'nil'.
463
+ #
464
+ it 'should render with correct default for whole hash (2)' do
465
+ result = TestHashSpecificKeyPresenterWithDefaults.render( nil )
466
+
467
+ expect( result ).to eq({
468
+ 'specific_defaults' => {
469
+ 'one' => 'anything',
470
+ 'two' => { 'foo' => 'valid', 'bar' => 'this is the text field for "bar"', 'baz' => 42 },
471
+ 'three' => { 'bar' => 'for_key_three', 'baz' => 42 }
472
+ }
473
+ })
474
+ end
475
+
476
+ # Empty objects gain default fields, just like at the root level;
477
+ # and key-level defaults (one is specified for key 'three') also end
478
+ # up with field-level defaults merged (almost by accident, but it
479
+ # makes sense to do so) - integer 'baz' has a default value of 42 as
480
+ # a field-level default.
481
+ #
482
+ it 'renders an explicit empty hash with default fields' do
483
+ data = { 'specific_defaults' => {} }
484
+ result = TestHashSpecificKeyPresenterWithDefaults.render( data )
485
+ expect( result ).to eq({
486
+ 'specific_defaults' => {
487
+ 'one' => { 'foo' => { 'bar' => 'baz' } },
488
+ 'three' => { 'bar' => 'for_key_three', 'baz' => 42 }
489
+ }
490
+ })
491
+ end
492
+
493
+ # ...but explicit nil means nil.
494
+ #
495
+ it 'renders an explicit nil' do
496
+ data = { 'specific_defaults' => nil }
497
+ result = TestHashSpecificKeyPresenterWithDefaults.render( data )
498
+ expect( result ).to eq(data)
499
+ end
500
+
501
+ # One of the hash's specific keys has a defined block with some default
502
+ # values, so if we specify an empty hash for that key, we expect it to
503
+ # be filled in with defaults.
504
+ #
505
+ it 'should render with correct defaults for hash value keys' do
506
+ data = { 'specific_defaults' => { 'two' => {} } }
507
+ result = TestHashSpecificKeyPresenterWithDefaults.render( data )
508
+
509
+ expect( result ).to eq({
510
+ 'specific_defaults' => {
511
+ 'one' => { 'foo' => { 'bar' => 'baz' } },
512
+ 'two' => {
513
+ 'bar' => 'this is the text field for "bar"',
514
+ 'baz' => 42
515
+ },
516
+ 'three' => { 'bar' => 'for_key_three', 'baz' => 42 }
517
+ }
518
+ })
519
+ end
520
+
521
+ # Take away the hash-level full default value; we expect an omitted hash
522
+ # to be rendered with default contents.
523
+ #
524
+ it 'should render with correct default for keys when hash itself has no default (1)' do
525
+ result = TestHashSpecificKeyPresenterWithDefaultsExceptHash.render( {} )
526
+
527
+ expect( result ).to eq({
528
+ 'specific_defaults' => {
529
+ 'one' => { 'foo' => { 'bar' => 'baz' } }
530
+ }
531
+ })
532
+ end
533
+
534
+ # Should behave the same with 'nil'.
535
+ #
536
+ it 'should render with correct default for keys when hash itself has no default (2)' do
537
+ result = TestHashSpecificKeyPresenterWithDefaultsExceptHash.render( nil )
538
+
539
+ expect( result ).to eq({
540
+ 'specific_defaults' => {
541
+ 'one' => { 'foo' => { 'bar' => 'baz' } }
542
+ }
543
+ })
544
+ end
545
+
546
+ # We also expect the same result if an empty hash is provided, as with the
547
+ # case where the hash itself had a default value.
548
+ #
549
+ it 'should render with correct defaults for hash keys (3)' do
550
+ data = { 'specific_defaults' => {} }
551
+ result = TestHashSpecificKeyPresenterWithDefaultsExceptHash.render( data )
552
+
553
+ expect( result ).to eq({
554
+ 'specific_defaults' => {
555
+ 'one' => { 'foo' => { 'bar' => 'baz' } }
556
+ }
557
+ })
558
+ end
559
+
560
+ # Once more, explicit nil means nil.
561
+ #
562
+ it 'should render with correct defaults for hash keys (4)' do
563
+ data = { 'specific_defaults' => nil }
564
+ result = TestHashSpecificKeyPresenterWithDefaultsExceptHash.render( data )
565
+
566
+ expect( result ).to eq(data)
567
+ end
568
+ end
569
+ end
570
+
571
+ ############################################################################
572
+
573
+ class TestHashGenericKeyPresenterNoValues < Hoodoo::Presenters::Base
574
+ schema do
575
+ hash :generic do
576
+ keys :length => 6
577
+ end
578
+ end
579
+ end
580
+
581
+ ############################################################################
582
+
583
+ context 'generic keys no values' do
584
+ context '#validate' do
585
+ it 'should return [] when valid object' do
586
+ data = { 'generic' => { 'one' => 'anything' } }
587
+ expect( TestHashGenericKeyPresenterNoValues.validate( data ).errors ).to eq( [] )
588
+ end
589
+
590
+ it 'should return correct errors with invalid keys' do
591
+ data = { 'generic' => { 'one' => 'anything',
592
+ 'two' => 'anything',
593
+ 'toolong' => 'anything',
594
+ 'evenlonger' => 'anything' } }
595
+ errors = [
596
+ {
597
+ 'code' => 'generic.invalid_string',
598
+ 'message' => 'Field `generic.toolong` is longer than maximum length `6`',
599
+ 'reference' => 'generic.toolong'
600
+ },
601
+ {
602
+ 'code' => 'generic.invalid_string',
603
+ 'message' => 'Field `generic.evenlonger` is longer than maximum length `6`',
604
+ 'reference' => 'generic.evenlonger'
605
+ }
606
+ ]
607
+
608
+ expect( TestHashGenericKeyPresenterNoValues.validate( data ).errors ).to eq( errors )
609
+ end
610
+ end
611
+
612
+ context '#render' do
613
+ it 'should render correctly' do
614
+
615
+ # Use of "values" is important as it tests for a collision with
616
+ # an internal same-named property. It's an implementation detail
617
+ # which the code should deal with internally correctly, leading
618
+ # to no disernable alteration in expected rendering for callers.
619
+
620
+ data = { 'generic' => { 'one' => 'anything 1',
621
+ 'values' => 'should not be overwritten',
622
+ 'two' => 'anything 2' } }
623
+
624
+ result = TestHashGenericKeyPresenterNoValues.render( data )
625
+ expect( result ).to eq( data )
626
+ end
627
+ end
628
+ end
629
+
630
+ ############################################################################
631
+
632
+ class TestHashGenericKeyPresenterWithValues < Hoodoo::Presenters::Base
633
+ schema do
634
+ hash :generic do
635
+ keys :length => 4 do
636
+ string :foo, :length => 10
637
+ text :bar
638
+ end
639
+ end
640
+ end
641
+ end
642
+
643
+ ############################################################################
644
+
645
+ context 'generic keys with values' do
646
+ context '#validate' do
647
+ it 'should return [] when valid object' do
648
+ data = { 'generic' => { 'one' => { 'foo' => 'foov' } } }
649
+ expect( TestHashGenericKeyPresenterWithValues.validate( data ).errors ).to eq( [] )
650
+ end
651
+
652
+ it 'should return correct errors with invalid keys' do
653
+ data = { 'generic' => { 'one' => { 'foo' => 'foov' },
654
+ 'two' => { 'bar' => 'barv' },
655
+ 'toolong' => { 'foo' => 'foov' },
656
+ 'evenlonger' => { 'bar' => 'barv' } } }
657
+ errors = [
658
+ {
659
+ 'code' => 'generic.invalid_string',
660
+ 'message' => 'Field `generic.toolong` is longer than maximum length `4`',
661
+ 'reference' => 'generic.toolong'
662
+ },
663
+ {
664
+ 'code' => 'generic.invalid_string',
665
+ 'message' => 'Field `generic.evenlonger` is longer than maximum length `4`',
666
+ 'reference' => 'generic.evenlonger'
667
+ }
668
+ ]
669
+
670
+ expect( TestHashGenericKeyPresenterWithValues.validate( data ).errors ).to eq( errors )
671
+ end
672
+
673
+ it 'should return correct errors with invalid values' do
674
+ data = { 'generic' => { 'one' => { 'foo' => 'foov' },
675
+ 'two' => 'not-an-object' } }
676
+ errors = [
677
+ {
678
+ 'code' => 'generic.invalid_object',
679
+ 'message' => 'Field `generic.two` is an invalid object',
680
+ 'reference' => 'generic.two'
681
+ }
682
+ ]
683
+
684
+ expect( TestHashGenericKeyPresenterWithValues.validate( data ).errors ).to eq( errors )
685
+ end
686
+
687
+ it 'should return correct errors with invalid keys and values' do
688
+ data = { 'generic' => { 'one' => 'not-an-object',
689
+ 'two' => { 'bar' => 'barv' },
690
+ 'toolong' => 'not-an-object',
691
+ 'evenlonger' => { 'bar' => 'barv' } } }
692
+ errors = [
693
+ {
694
+ 'code' => 'generic.invalid_object',
695
+ 'message' => 'Field `generic.one` is an invalid object',
696
+ 'reference' => 'generic.one'
697
+ },
698
+ {
699
+ 'code' => 'generic.invalid_string',
700
+ 'message' => 'Field `generic.toolong` is longer than maximum length `4`',
701
+ 'reference' => 'generic.toolong'
702
+ },
703
+ {
704
+ 'code' => 'generic.invalid_object',
705
+ 'message' => 'Field `generic.toolong` is an invalid object',
706
+ 'reference' => 'generic.toolong'
707
+ },
708
+ {
709
+ 'code' => 'generic.invalid_string',
710
+ 'message' => 'Field `generic.evenlonger` is longer than maximum length `4`',
711
+ 'reference' => 'generic.evenlonger'
712
+ }
713
+ ]
714
+
715
+ expect( TestHashGenericKeyPresenterWithValues.validate( data ).errors ).to eq( errors )
716
+ end
717
+ end
718
+
719
+ context '#render' do
720
+ it 'should only render expected fields' do
721
+
722
+ valid = { 'generic' => { 'one' => { 'foo' => '<= 10 long' }, 'values' => { 'foo' => '<= 10 2', 'bar' => 'barv' }, 'two' => { 'bar' => 'barv 2' } } }
723
+ data = { 'generic' => { 'one' => { 'random' => 'ignore', 'foo' => '<= 10 long' }, 'values' => { 'foo' => '<= 10 2', 'bar' => 'barv', 'hello' => 'there' }, 'two' => { 'bar' => 'barv 2' } } }
724
+
725
+ result = TestHashGenericKeyPresenterWithValues.render( data )
726
+ expect( result ).to eq( valid )
727
+ end
728
+ end
729
+ end
730
+
731
+ context 'generic keys no values' do
732
+ context '#validate' do
733
+ it 'should return [] when valid object' do
734
+ data = { 'generic' => { 'one' => 'anything' } }
735
+ expect( TestHashGenericKeyPresenterNoValues.validate( data ).errors ).to eq( [] )
736
+ end
737
+
738
+ it 'should return correct errors with invalid keys' do
739
+ data = { 'generic' => { 'one' => 'anything',
740
+ 'two' => 'anything',
741
+ 'toolong' => 'anything',
742
+ 'evenlonger' => 'anything' } }
743
+ errors = [
744
+ {
745
+ 'code' => 'generic.invalid_string',
746
+ 'message' => 'Field `generic.toolong` is longer than maximum length `6`',
747
+ 'reference' => 'generic.toolong'
748
+ },
749
+ {
750
+ 'code' => 'generic.invalid_string',
751
+ 'message' => 'Field `generic.evenlonger` is longer than maximum length `6`',
752
+ 'reference' => 'generic.evenlonger'
753
+ }
754
+ ]
755
+
756
+ expect( TestHashGenericKeyPresenterNoValues.validate( data ).errors ).to eq( errors )
757
+ end
758
+ end
759
+
760
+ context '#render' do
761
+ it 'should render correctly' do
762
+
763
+ # Use of "values" is important as it tests for a collision with
764
+ # an internal same-named property. It's an implementation detail
765
+ # which the code should deal with internally correctly, leading
766
+ # to no disernable alteration in expected rendering for callers.
767
+
768
+ data = { 'generic' => { 'one' => 'anything 1',
769
+ 'values' => 'should not be overwritten',
770
+ 'two' => 'anything 2' } }
771
+
772
+ result = TestHashGenericKeyPresenterNoValues.render( data )
773
+ expect( result ).to eq( data )
774
+ end
775
+ end
776
+ end
777
+
778
+ ############################################################################
779
+
780
+ class TestNestedHashGenericKeyPresenterWithValues < Hoodoo::Presenters::Base
781
+ schema do
782
+ object :obj do
783
+ hash :generic do
784
+ keys :length => 4 do
785
+ string :foo, :length => 10
786
+ text :bar
787
+ hash :baz do
788
+ keys do
789
+ string :inner_string, :length => 5
790
+ end
791
+ end
792
+ end
793
+ end
794
+ end
795
+ end
796
+ end
797
+
798
+ ############################################################################
799
+
800
+ context 'nested generic keys with values' do
801
+ context '#validate' do
802
+ it 'should return [] when valid object' do
803
+ data = { 'obj' => { 'generic' => { 'one' => { 'foo' => 'foov' } } } }
804
+ expect( TestNestedHashGenericKeyPresenterWithValues.validate( data ).errors ).to eq( [] )
805
+ end
806
+
807
+ it 'should return correct errors with invalid keys' do
808
+ data = { 'obj' => { 'generic' => { 'one' => { 'foo' => 'foov' },
809
+ 'two' => { 'bar' => 'barv' },
810
+ 'toolong' => { 'foo' => 'foov' },
811
+ 'evenlonger' => { 'bar' => 'barv' },
812
+ 'ok' => { 'baz' => { 'anything' => { 'inner_string' => 'toolong' } } } } } }
813
+ errors = [
814
+ {
815
+ 'code' => 'generic.invalid_string',
816
+ 'message' => 'Field `obj.generic.toolong` is longer than maximum length `4`',
817
+ 'reference' => 'obj.generic.toolong'
818
+ },
819
+ {
820
+ 'code' => 'generic.invalid_string',
821
+ 'message' => 'Field `obj.generic.evenlonger` is longer than maximum length `4`',
822
+ 'reference' => 'obj.generic.evenlonger'
823
+ },
824
+ {
825
+ 'code' => 'generic.invalid_string',
826
+ 'message' => 'Field `obj.generic.ok.baz.anything.inner_string` is longer than maximum length `5`',
827
+ 'reference' => 'obj.generic.ok.baz.anything.inner_string'
828
+ }
829
+ ]
830
+
831
+ expect( TestNestedHashGenericKeyPresenterWithValues.validate( data ).errors ).to eq( errors )
832
+ end
833
+ end
834
+
835
+ context '#render' do
836
+ it 'should only render expected fields' do
837
+
838
+ valid = { 'obj' => { 'generic' => { 'one' => { 'foo' => '<= 10 long' }, 'values' => { 'foo' => '<= 10 2', 'bar' => 'barv', 'baz' => { 'any' => { 'inner_string' => 'hi' } } }, 'two' => { 'bar' => 'barv 2' } } } }
839
+ data = { 'number' => 42, 'obj' => { 'generic' => { 'one' => { 'random' => 'ignore', 'foo' => '<= 10 long' }, 'values' => { 'foo' => '<= 10 2', 'bar' => 'barv', 'baz' => { 'any' => { 'hi' => 'there', 'inner_string' => 'hi' } }, 'hello' => 'there' }, 'two' => { 'bar' => 'barv 2' } } } }
840
+
841
+ result = TestNestedHashGenericKeyPresenterWithValues.render( data )
842
+ expect( result ).to eq( valid )
843
+ end
844
+ end
845
+ end
846
+
847
+ ############################################################################
848
+
849
+ class TestHashGenericKeyPresenterWithRequirements < Hoodoo::Presenters::Base
850
+ schema do
851
+ hash :generic do
852
+ keys :length => 4, :required => true do
853
+ string :foo, :length => 10
854
+ text :bar, :required => true
855
+ end
856
+ end
857
+ end
858
+ end
859
+
860
+ ############################################################################
861
+
862
+ context 'generic keys with requirements' do
863
+ context '#validate' do
864
+ it 'should return [] when valid object with keys' do
865
+ data = { 'generic' => { 'one' => { 'foo' => 'foov', 'bar' => 'hello' } } }
866
+ expect( TestHashGenericKeyPresenterWithRequirements.validate( data ).errors ).to eq( [] )
867
+ end
868
+
869
+ # The Hash itself isn't required; only its keys are, so if 'nil' that
870
+ # should not cause an error.
871
+ #
872
+ it 'should return [] with valid missing hash' do
873
+ data = { 'generic' => nil }
874
+ expect( TestHashGenericKeyPresenterWithRequirements.validate( data ).errors ).to eq( [] )
875
+ end
876
+
877
+ # The Hash itself isn't required; only its keys are, so if present but
878
+ # empty (no keys), that should cause an error.
879
+ #
880
+ it 'should return correct errors with missing hash' do
881
+ data = { 'generic' => {} }
882
+ errors = [
883
+ {
884
+ 'code' => 'generic.required_field_missing',
885
+ 'message' => 'Field `generic` is required (Hash, if present, must contain at least one key)',
886
+ 'reference' => 'generic'
887
+ }
888
+ ]
889
+
890
+ expect( TestHashGenericKeyPresenterWithRequirements.validate( data ).errors ).to eq( errors )
891
+ end
892
+
893
+ it 'should return correct errors with missing keys in hash' do
894
+ data = { 'generic' => { 'one' => { 'foo' => 'foov' } } }
895
+ errors = [
896
+ {
897
+ 'code' => 'generic.required_field_missing',
898
+ 'message' => 'Field `generic.one.bar` is required',
899
+ 'reference' => 'generic.one.bar'
900
+ }
901
+ ]
902
+
903
+ expect( TestHashGenericKeyPresenterWithRequirements.validate( data ).errors ).to eq( errors )
904
+ end
905
+ end
906
+ end
907
+
908
+ ############################################################################
909
+
910
+ it 'complains about generic default keys as they are meaningless' do
911
+ expect {
912
+ class TestHashGenericKeyPresenterWithMeaninglessDefaults < Hoodoo::Presenters::Base
913
+ schema do
914
+ hash :generic_defaults do
915
+ keys :length => 4, :default => { 'meaningless' => 'complain' } do
916
+ text :baz
917
+ end
918
+ end
919
+ end
920
+ end
921
+ }.to raise_error(RuntimeError)
922
+ end
923
+
924
+ class TestHashGenericKeyPresenterWithDefaults < Hoodoo::Presenters::Base
925
+ schema do
926
+ hash :generic_defaults, :default => { 'a_default_key' => { 'baz' => 'merge' }, 'a_nil_key' => nil } do
927
+ keys :length => 4 do
928
+ string :foo, :length => 10
929
+ text :bar, :default => 'default for text field'
930
+ text :baz
931
+ end
932
+ end
933
+ end
934
+ end
935
+
936
+ class TestHashGenericKeyPresenterWithDefaultsExceptHash < Hoodoo::Presenters::Base
937
+ schema do
938
+ hash :generic_defaults do
939
+ keys :length => 4 do
940
+ string :foo, :length => 10
941
+ text :bar, :default => 'default for text field'
942
+ text :baz
943
+ end
944
+ end
945
+ end
946
+ end
947
+
948
+ ############################################################################
949
+
950
+ context 'generic keys with defaults' do
951
+ context '#render' do
952
+
953
+ # The hash itself has a default set, so if the hash key is omitted we
954
+ # expect to get the canned default hash instead. This outer default is
955
+ # rendered, so the key(s) it specifies get individually run through
956
+ # the renderer. If the value is nil, it'll gain the whole-value default.
957
+ # Otherwise, it gains in-block defaults (if any).
958
+ #
959
+ it 'should render with correct default for whole hash (1)' do
960
+ result = TestHashGenericKeyPresenterWithDefaults.render( {} )
961
+
962
+ expect( result ).to eq({
963
+ 'generic_defaults' => {
964
+ 'a_default_key' => { 'bar' => 'default for text field', 'baz' => 'merge' },
965
+ 'a_nil_key' => nil
966
+ }
967
+ })
968
+ end
969
+
970
+ # Explicit nil means nil, but at the top level we have to treat it as an
971
+ # empty hash so it'll give the same result.
972
+ #
973
+ it 'should render with correct default for whole hash (2)' do
974
+ result = TestHashGenericKeyPresenterWithDefaults.render( nil )
975
+
976
+ expect( result ).to eq({
977
+ 'generic_defaults' => {
978
+ 'a_default_key' => { 'bar' => 'default for text field', 'baz' => 'merge' },
979
+ 'a_nil_key' => nil
980
+ }
981
+ })
982
+ end
983
+
984
+ # If we explicitly give a value for the hash, then that should override
985
+ # the hash-wide default, even if empty...
986
+ #
987
+ it 'should render with correct defaults for hash keys (1)' do
988
+ data = { 'generic_defaults' => {} }
989
+ result = TestHashGenericKeyPresenterWithDefaults.render( data )
990
+
991
+ expect( result ).to eq({
992
+ 'generic_defaults' => {}
993
+ })
994
+ end
995
+
996
+ # ...However explicit nil means nil...
997
+ #
998
+ it 'should render with correct defaults for hash keys (2)' do
999
+ data = { 'generic_defaults' => nil }
1000
+ result = TestHashGenericKeyPresenterWithDefaults.render( data )
1001
+ expect( result ).to eq(data)
1002
+ end
1003
+
1004
+ # ...and explicitly defined keys with nil values should be nil too.
1005
+ #
1006
+ it 'should render with correct defaults for hash keys (3)' do
1007
+ data = { 'generic_defaults' => { 'hello' => nil, 'goodbye' => {}, 'another' => { 'foo' => 'present', 'bar' => 'also present' } } }
1008
+ result = TestHashGenericKeyPresenterWithDefaults.render( data )
1009
+
1010
+ expect( result ).to eq({
1011
+ 'generic_defaults' => {
1012
+ 'hello' => nil,
1013
+ 'goodbye' => { 'bar' => 'default for text field' },
1014
+ 'another' => { 'foo' => 'present', 'bar' => 'also present' }
1015
+ }
1016
+ })
1017
+ end
1018
+
1019
+ # No hash default now. An empty hash can only be rendered as an empty
1020
+ # hash, because we don't have any default key names or hash-wide default
1021
+ # to use as a template.
1022
+ #
1023
+ it 'should render with correct default for whole hash (1)' do
1024
+ result = TestHashGenericKeyPresenterWithDefaultsExceptHash.render( {} )
1025
+ expect( result ).to eq({})
1026
+ end
1027
+
1028
+ # Should behave the same with 'nil'.
1029
+ #
1030
+ it 'should render with correct default for whole hash (2)' do
1031
+ result = TestHashGenericKeyPresenterWithDefaultsExceptHash.render( nil )
1032
+ expect( result ).to eq({})
1033
+ end
1034
+
1035
+ # If we explicitly give a value for the hash, should get the empty hash
1036
+ # recorded...
1037
+ #
1038
+ it 'should render with correct defaults for hash keys (1)' do
1039
+ data = { 'generic_defaults' => {} }
1040
+ result = TestHashGenericKeyPresenterWithDefaultsExceptHash.render( data )
1041
+ expect( result ).to eq({ 'generic_defaults' => {} })
1042
+ end
1043
+
1044
+ # ...explicit nil means nil...
1045
+ #
1046
+ it 'should render with correct defaults for hash keys (2)' do
1047
+ data = { 'generic_defaults' => nil }
1048
+ result = TestHashGenericKeyPresenterWithDefaultsExceptHash.render( data )
1049
+ expect( result ).to eq({ 'generic_defaults' => nil })
1050
+ end
1051
+
1052
+ # ...and when we have keys supplied, the usual default rules should apply.
1053
+ #
1054
+ it 'should render with correct defaults for hash keys (3)' do
1055
+ data = { 'generic_defaults' => { 'hello' => nil, 'goodbye' => {}, 'another' => { 'foo' => 'present', 'bar' => 'also present' } } }
1056
+ result = TestHashGenericKeyPresenterWithDefaultsExceptHash.render( data )
1057
+
1058
+ expect( result ).to eq({
1059
+ 'generic_defaults' => {
1060
+ 'hello' => nil,
1061
+ 'goodbye' => { 'bar' => 'default for text field' },
1062
+ 'another' => { 'foo' => 'present', 'bar' => 'also present' }
1063
+ }
1064
+ })
1065
+ end
1066
+ end
1067
+ end
1068
+
1069
+ end