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,17 @@
1
+ ########################################################################
2
+ # File:: version.rb
3
+ # (C):: Loyalty New Zealand 2015
4
+ #
5
+ # Purpose:: Declare this gem's version number.
6
+ # ----------------------------------------------------------------------
7
+ # 27-Jan-2015 (ADH): Created (for gem under name Hoodoo).
8
+ ########################################################################
9
+
10
+ module Hoodoo
11
+
12
+ # The Hoodoo gem version. If this changes, ensure that the date in
13
+ # "hoodoo.gemspec" is correct and run "bundle install" (or "update").
14
+ #
15
+ VERSION = '1.0.2'
16
+
17
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe Hoodoo::ActiveRecord::Base do
5
+ before :all do
6
+ spec_helper_silence_stdout() do
7
+ tblname = :r_spec_model_base_tests
8
+
9
+ ActiveRecord::Migration.create_table( tblname, :id => false ) do | t |
10
+ t.string( :id, :limit => 32, :null => false )
11
+ end
12
+
13
+ ActiveRecord::Migration.add_index( tblname, :id, :unique => true )
14
+
15
+ class RSpecModelBaseTest < Hoodoo::ActiveRecord::Base
16
+ end
17
+ end
18
+ end
19
+
20
+ it 'has security capability' do
21
+ expect( RSpecModelBaseTest ).to respond_to( :secure )
22
+ end
23
+
24
+ it 'has dating capability' do
25
+ expect( RSpecModelBaseTest ).to respond_to( :dated )
26
+ end
27
+
28
+ it 'has internationalisation capability' do
29
+ expect( RSpecModelBaseTest ).to respond_to( :translated )
30
+ end
31
+
32
+ it 'finds things' do
33
+ m = RSpecModelBaseTest.new
34
+ m.save
35
+ expect( RSpecModelBaseTest.acquire( m.id ) ).to_not be_nil
36
+ end
37
+
38
+ it 'gains a UUID' do
39
+ m = RSpecModelBaseTest.new
40
+ m.save
41
+
42
+ expect( m.id ).to_not be_nil
43
+ expect( Hoodoo::UUID.valid?( m.id ) ).to eq( true )
44
+ end
45
+
46
+ it 'has creator capability' do
47
+ expect( RSpecModelBaseTest ).to respond_to( :new_in )
48
+ end
49
+
50
+ it 'has writer capability' do
51
+ expect( RSpecModelBaseTest ).to respond_to( :persist_in )
52
+ end
53
+
54
+ it 'supports error mapping' do
55
+ expect( RSpecModelBaseTest.new.platform_errors.has_errors? ).to eq( false )
56
+ end
57
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe Hoodoo::ActiveRecord::Creator do
5
+ before :all do
6
+ spec_helper_silence_stdout() do
7
+ ActiveRecord::Migration.create_table( :r_spec_model_creator_tests, :id => :string ) do | t |
8
+ t.text :code
9
+ t.text :field_one
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+
15
+ class RSpecModelCreatorTest < ActiveRecord::Base
16
+ include Hoodoo::ActiveRecord::Creator
17
+ include Hoodoo::ActiveRecord::Dated
18
+ end
19
+ end
20
+
21
+ # ==========================================================================
22
+
23
+ context 'new_in' do
24
+ before :each do
25
+
26
+ # Get a good-enough-for-test interaction which has a context
27
+ # that contains a Session we can modify.
28
+
29
+ @interaction = Hoodoo::Services::Middleware::Interaction.new( {}, nil )
30
+ @interaction.context = Hoodoo::Services::Context.new(
31
+ Hoodoo::Services::Session.new,
32
+ @interaction.context.request,
33
+ @interaction.context.response,
34
+ @interaction
35
+ )
36
+
37
+ @context = @interaction.context
38
+ @session = @interaction.context.session
39
+ end
40
+
41
+ it 'creates with no special values' do
42
+ instance = RSpecModelCreatorTest.new_in( @context )
43
+
44
+ expect( instance.created_at ).to be_nil
45
+ expect( instance.updated_at ).to be_nil
46
+ end
47
+
48
+ it 'creates with specified dated-from value' do
49
+ time = Time.now.iso8601
50
+ @context.request.dated_from = time
51
+
52
+ instance = RSpecModelCreatorTest.new_in( @context )
53
+
54
+ expect( instance.created_at.iso8601 ).to eq( time )
55
+ expect( instance.updated_at.iso8601 ).to eq( time )
56
+ end
57
+
58
+ it 'creates with provided attributes' do
59
+ instance = RSpecModelCreatorTest.new_in( @context, code: 'code', field_one: 'one' )
60
+
61
+ expect( instance.code ).to eq( 'code' )
62
+ expect( instance.field_one ).to eq( 'one' )
63
+ end
64
+
65
+ it 'creates with a block' do
66
+ instance = RSpecModelCreatorTest.new_in( @context ) do | i |
67
+ i.code = 'code'
68
+ i.field_one = 'one'
69
+ end
70
+
71
+ expect( instance.code ).to eq( 'code' )
72
+ expect( instance.field_one ).to eq( 'one' )
73
+ end
74
+
75
+ # Technically ActiveRecord documentation seems to indicate this is an
76
+ # either-or choice, but the code certainly supports both and it isn't
77
+ # *explicitly* stated as such, so we assume both can be mixed here.
78
+ #
79
+ it 'creates with provided attributes and a block' do
80
+ instance = RSpecModelCreatorTest.new_in( @context, code: 'code' ) do | i |
81
+ i.field_one = 'one'
82
+ end
83
+
84
+ expect( instance.code ).to eq( 'code' )
85
+ expect( instance.field_one ).to eq( 'one' )
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,248 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe Hoodoo::ActiveRecord::Dated do
5
+
6
+ before :all do
7
+ spec_helper_silence_stdout() do
8
+
9
+ ActiveRecord::Migration.create_table( :r_spec_model_effective_date_tests, :id => false ) do | t |
10
+ t.text :id, :null => false
11
+ t.text :data
12
+ t.timestamps
13
+ end
14
+
15
+ ActiveRecord::Migration.create_table( :r_spec_model_effective_date_tests_history_entries, :id => false ) do | t |
16
+ t.text :id, :null => false
17
+ t.text :uuid, :null => false
18
+ t.text :data
19
+ t.datetime :effective_start, :null => false
20
+ t.datetime :effective_end, :null => false
21
+ t.timestamps
22
+ end
23
+
24
+ class RSpecModelEffectiveDateTest < ActiveRecord::Base
25
+ include Hoodoo::ActiveRecord::Dated
26
+ dating_enabled()
27
+ end
28
+
29
+ ActiveRecord::Migration.create_table( :r_spec_model_effective_date_test_overrides, :id => false ) do | t |
30
+ t.text :id, :null => false
31
+ t.text :data
32
+ t.timestamps
33
+ end
34
+
35
+ ActiveRecord::Migration.create_table( :r_spec_model_effective_date_history_entries, :id => false ) do | t |
36
+ t.text :id, :null => false
37
+ t.text :uuid, :null => false
38
+ t.text :data
39
+ t.datetime :effective_start, :null => false
40
+ t.datetime :effective_end, :null => false
41
+ t.timestamps
42
+ end
43
+
44
+ class RSpecModelEffectiveDateTestOverride < ActiveRecord::Base
45
+ include Hoodoo::ActiveRecord::Dated
46
+
47
+ dating_enabled( :history_table_name => :r_spec_model_effective_date_history_entries )
48
+ end
49
+ end
50
+ end
51
+
52
+ shared_examples Hoodoo::ActiveRecord::Dated do
53
+
54
+ before( :all ) do
55
+
56
+ # Create some examples data for finding. The data has two different UUIDs
57
+ # which I'll referer to as A and B. The following tables contain the
58
+ # historical and current records separately with their attributes.
59
+ #
60
+ # Historical:
61
+ # -------------------------------------------------------------------
62
+ # uuid | data | created_at | effective_end | effective_start |
63
+ # -------------------------------------------------------------------
64
+ # A | "one" | now - 5 hours | now - 3 hours | now - 5 hours |
65
+ # B | "two" | now - 4 hours | now - 2 hours | now - 4 hours |
66
+ # A | "three" | now - 5 hours | now - 1 hour | now - 3 hours |
67
+ # B | "four" | now - 4 hours | now | now - 2 hour |
68
+ #
69
+ # Current:
70
+ # --------------------------------
71
+ # uuid | data | created_at |
72
+ # --------------------------------
73
+ # B | "five" | now - 4 hours |
74
+ # A | "six" | now - 5 hours |
75
+ #
76
+
77
+ @uuid_a = Hoodoo::UUID.generate
78
+ @uuid_b = Hoodoo::UUID.generate
79
+
80
+ @now = Time.now.utc
81
+
82
+ # uuid, data, created_at, effective_end, effective_start
83
+ [
84
+ [ @uuid_a, "one", @now - 5.hours, @now - 3.hours, @now - 5.hours ],
85
+ [ @uuid_b, "two", @now - 4.hours, @now - 2.hours, @now - 4.hours ],
86
+ [ @uuid_a, "three", @now - 5.hours, @now - 1.hour, @now - 3.hours ],
87
+ [ @uuid_b, "four", @now - 4.hours, @now, @now - 2.hours ]
88
+ ].each do | row_data |
89
+ model_klass.dated_with.new( {
90
+ :id => row_data[ 0 ] + "-" + row_data[ 3 ].iso8601,
91
+ :uuid => row_data[ 0 ],
92
+ :data => row_data[ 1 ],
93
+ :created_at => row_data[ 2 ],
94
+ :effective_end => row_data[ 3 ],
95
+ :effective_start => row_data[ 4 ]
96
+ } ).save!
97
+ end
98
+
99
+ # uuid, data, created_at, updated_at
100
+ [
101
+ [ @uuid_b, "five", @now - 4.hours, @now ],
102
+ [ @uuid_a, "six", @now - 5.hours, @now - 1.hour ]
103
+ ].each do | row_data |
104
+ model_klass.new( {
105
+ :id => row_data[ 0 ],
106
+ :data => row_data[ 1 ],
107
+ :created_at => row_data[ 2 ],
108
+ :updated_at => row_data[ 3 ]
109
+ } ).save!
110
+ end
111
+
112
+ end
113
+
114
+ context '.dated_at' do
115
+ it 'returns counts correctly' do
116
+ expect( model_klass.dated_at( @now - 10.hours ).count ).to be 0
117
+ expect( model_klass.dated_at( @now ).count ).to be 2
118
+ end
119
+
120
+ def test_expectation( time, expected_data )
121
+ expect( model_klass.dated_at( time ).pluck( :data ) ).to match_array( expected_data )
122
+ end
123
+
124
+ it 'returns no records before any were effective' do
125
+ test_expectation( @now - 10.hours, [] )
126
+ end
127
+
128
+ it 'returns records that used to be effective starting at past time' do
129
+ test_expectation( @now - 5.hours, [ "one" ] )
130
+ test_expectation( @now - 4.hours, [ "one", "two" ] )
131
+ test_expectation( @now - 3.hours, [ "two", "three" ] )
132
+ test_expectation( @now - 2.hours, [ "three", "four" ] )
133
+ test_expectation( @now - 1.hour, [ "four", "six" ] )
134
+ end
135
+
136
+ it 'returns records that are effective now' do
137
+ test_expectation( @now, [ "five", "six" ] )
138
+ end
139
+
140
+ it 'works with further filtering' do
141
+ expect( model_klass.dated_at( @now ).where( :id => @uuid_a ).pluck( :data ) ).to eq( [ "six" ] )
142
+ end
143
+
144
+ end
145
+
146
+ context '.dated' do
147
+ it 'returns counts correctly' do
148
+ # The contents of the Context are irrelevant aside from the fact that it
149
+ # needs a request to store the dated_at value.
150
+ request = Hoodoo::Services::Request.new
151
+ context = Hoodoo::Services::Context.new( nil, request, nil, nil )
152
+
153
+ context.request.dated_at = @now - 10.hours
154
+ expect( model_klass.dated( context ).count ).to be 0
155
+
156
+ context.request.dated_at = @now
157
+ expect( model_klass.dated( context ).count ).to be 2
158
+ end
159
+
160
+ def test_expectation( time, expected_data )
161
+ # The contents of the Context are irrelevant aside from the fact that it
162
+ # needs a request to store the dated_at value.
163
+ request = Hoodoo::Services::Request.new
164
+ context = Hoodoo::Services::Context.new( nil, request, nil, nil )
165
+ context.request.dated_at = time
166
+
167
+ expect( model_klass.dated( context ).pluck( :data ) ).to match_array( expected_data )
168
+ end
169
+
170
+ it 'returns no records before any were effective' do
171
+ test_expectation( @now - 10.hours, [] )
172
+ end
173
+
174
+ it 'returns records that used to be effective starting at past time' do
175
+ test_expectation( @now - 5.hours, [ "one" ] )
176
+ test_expectation( @now - 4.hours, [ "one", "two" ] )
177
+ test_expectation( @now - 3.hours, [ "two", "three" ] )
178
+ test_expectation( @now - 2.hours, [ "three", "four" ] )
179
+ test_expectation( @now - 1.hour, [ "four", "six" ] )
180
+ end
181
+
182
+ it 'returns records that are effective now' do
183
+ test_expectation( @now, [ "five", "six" ] )
184
+ end
185
+
186
+ it 'works with further filtering' do
187
+
188
+ # The contents of the Context are irrelevant aside from the fact that it
189
+ # needs a request to store the dated_at value.
190
+ request = Hoodoo::Services::Request.new
191
+ context = Hoodoo::Services::Context.new( nil, request, nil, nil )
192
+ context.request.dated_at = @now
193
+
194
+ expect( model_klass.dated( context ).where( :id => @uuid_a ).pluck( :data ) ).to eq( [ "six" ] )
195
+ end
196
+
197
+ it 'works with dating last' do
198
+
199
+ # The contents of the Context are irrelevant aside from the fact that it
200
+ # needs a request to store the dated_at value.
201
+ request = Hoodoo::Services::Request.new
202
+ context = Hoodoo::Services::Context.new( nil, request, nil, nil )
203
+ context.request.dated_at = @now
204
+
205
+ expect( model_klass.where( :id => @uuid_a ).dated( context ).pluck( :data ) ).to eq( [ "six" ] )
206
+ end
207
+
208
+ end
209
+
210
+ context '.dated_historical_and_current' do
211
+
212
+ it 'returns counts correctly' do
213
+ expect( model_klass.dated_historical_and_current.count ).to be 6
214
+ end
215
+
216
+ it 'lists all historical and current records' do
217
+ expect( model_klass.dated_historical_and_current.pluck( :data ) ).to match_array( [ 'one', 'two', 'three', 'four', 'five', 'six' ] )
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+
224
+ context "using default effective dating config" do
225
+
226
+ # Must be defined as a method rather than using a let statement as let
227
+ # statement values cannot be used in before blocks.
228
+ def model_klass
229
+ RSpecModelEffectiveDateTest
230
+ end
231
+
232
+ it_behaves_like Hoodoo::ActiveRecord::Dated
233
+
234
+ end
235
+
236
+ context "overriding history table name" do
237
+
238
+ # Must be defined as a method rather than using a let statement as let
239
+ # statement values cannot be used in before blocks.
240
+ def model_klass
241
+ RSpecModelEffectiveDateTestOverride
242
+ end
243
+
244
+ it_behaves_like Hoodoo::ActiveRecord::Dated
245
+
246
+ end
247
+
248
+ end
@@ -0,0 +1,360 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe Hoodoo::ActiveRecord::ErrorMapping do
5
+ before :all do
6
+ spec_helper_silence_stdout() do
7
+ ActiveRecord::Migration.create_table( :r_spec_model_error_mapping_tests ) do | t |
8
+ t.string :uuid
9
+ t.boolean :boolean
10
+ t.date :date
11
+ t.datetime :datetime
12
+ t.decimal :decimal, :precision => 5, :scale => 2
13
+ t.float :float
14
+ t.integer :integer
15
+ t.string :string, :limit => 16
16
+ t.text :text
17
+ t.time :time
18
+ t.text :array, :array => true
19
+ end
20
+
21
+ ActiveRecord::Migration.create_table( :r_spec_model_associated_error_mapping_tests ) do | t |
22
+ t.string :other_string
23
+
24
+ # For 'has_many' - can't use "t.reference" as the generated
25
+ # column name is too long!
26
+ #
27
+ t.integer :many_id
28
+ t.index :many_id
29
+ end
30
+
31
+ class RSpecModelErrorMappingTest < ActiveRecord::Base
32
+ include Hoodoo::ActiveRecord::ErrorMapping
33
+
34
+ has_many :r_spec_model_associated_error_mapping_tests,
35
+ :foreign_key => :many_id,
36
+ :class_name => :RSpecModelAssociatedErrorMappingTest
37
+
38
+ accepts_nested_attributes_for :r_spec_model_associated_error_mapping_tests
39
+
40
+ validates_presence_of :boolean,
41
+ :date,
42
+ :datetime,
43
+ :decimal,
44
+ :float,
45
+ :integer,
46
+ :string,
47
+ :text,
48
+ :time,
49
+ :array
50
+
51
+ validates_uniqueness_of :integer
52
+ validates :string, :length => { :maximum => 16 }
53
+ validates :uuid, :uuid => true
54
+
55
+ validate do
56
+ if string == 'magic'
57
+ errors.add( 'this.thing', 'is not a column' )
58
+ end
59
+ end
60
+ end
61
+
62
+ class RSpecModelAssociatedErrorMappingTest < ActiveRecord::Base
63
+ belongs_to :r_spec_model_error_mapping_test,
64
+ :foreign_key => :many_id,
65
+ :class_name => :RSpecModelErrorMappingTest
66
+
67
+ validates :other_string, :length => { :maximum => 6 }
68
+ end
69
+
70
+ class RSpecModelErrorMappingTestBase < ActiveRecord::Base
71
+ self.table_name = RSpecModelErrorMappingTest.table_name
72
+
73
+ include Hoodoo::ActiveRecord::ErrorMapping
74
+
75
+ validate do | instance |
76
+ instance.errors.add( :base, 'this is a test' )
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ before :each do
83
+ @errors = Hoodoo::Errors.new( Hoodoo::ErrorDescriptions.new )
84
+ end
85
+
86
+ let( :valid_model ) {
87
+ RSpecModelErrorMappingTest.new( {
88
+ :uuid => Hoodoo::UUID.generate(),
89
+ :boolean => true,
90
+ :date => Time.now,
91
+ :datetime => Time.now,
92
+ :decimal => 2.3,
93
+ :float => 2.3,
94
+ :integer => 42,
95
+ :string => "hello",
96
+ :text => "hello",
97
+ :time => Time.now,
98
+ :array => [ 'hello' ]
99
+ } )
100
+ }
101
+
102
+ it 'auto-validates and maps errors correctly' do
103
+
104
+ m = RSpecModelErrorMappingTest.new( :uuid => 'not a valid UUID' )
105
+ expect( m.adds_errors_to?( @errors ) ).to eq( true )
106
+
107
+ expect( @errors.errors ).to eq( [
108
+ {
109
+ "code" => "generic.invalid_boolean",
110
+ "message" => "can't be blank",
111
+ "reference" => "boolean"
112
+ },
113
+ {
114
+ "code" => "generic.invalid_date",
115
+ "message" => "can't be blank",
116
+ "reference" => "date"
117
+ },
118
+ {
119
+ "code" => "generic.invalid_datetime",
120
+ "message" => "can't be blank",
121
+ "reference" => "datetime"
122
+ },
123
+ {
124
+ "code" => "generic.invalid_decimal",
125
+ "message" => "can't be blank",
126
+ "reference" => "decimal"
127
+ },
128
+ {
129
+ "code" => "generic.invalid_float",
130
+ "message" => "can't be blank",
131
+ "reference" => "float"
132
+ },
133
+ {
134
+ "code" => "generic.invalid_integer",
135
+ "message" => "can't be blank",
136
+ "reference" => "integer"
137
+ },
138
+ {
139
+ "code" => "generic.invalid_string",
140
+ "message" => "can't be blank",
141
+ "reference" => "string"
142
+ },
143
+ {
144
+ "code" => "generic.invalid_string",
145
+ "message" => "can't be blank",
146
+ "reference" => "text"
147
+ },
148
+ {
149
+ "code" => "generic.invalid_time",
150
+ "message" => "can't be blank",
151
+ "reference" => "time"
152
+ },
153
+ {
154
+ "code" => "generic.invalid_array",
155
+ "message" => "can't be blank",
156
+ "reference" => "array"
157
+ },
158
+
159
+ # Checks that custom UUID validator is working.
160
+
161
+ {
162
+ "code" => "generic.invalid_uuid",
163
+ "message" => "is invalid",
164
+ "reference" => "uuid"
165
+ }
166
+ ] )
167
+ end
168
+
169
+ it 'maps "base" errors correctly' do
170
+ m = RSpecModelErrorMappingTestBase.new
171
+
172
+ expect( m.adds_errors_to?( @errors ) ).to eq( true )
173
+ expect( @errors.errors ).to eq( [
174
+ {
175
+ "code" => "generic.invalid_parameters",
176
+ "message" => "this is a test",
177
+ "reference" => "model instance"
178
+ }
179
+ ] )
180
+ end
181
+
182
+ it 'handles varying validation types' do
183
+ m = RSpecModelErrorMappingTest.new( {
184
+ :boolean => true,
185
+ :date => Time.now,
186
+ :datetime => Time.now,
187
+ :decimal => 2.3,
188
+ :float => 2.3,
189
+ :integer => 42,
190
+ :string => "hello - this is far too long for the maximum field length",
191
+ :text => "hello",
192
+ :time => Time.now,
193
+ :array => [ 'hello' ]
194
+ } )
195
+
196
+ m.adds_errors_to?( @errors )
197
+ expect( @errors.errors ).to eq( [
198
+ {
199
+ "code" => "generic.invalid_string",
200
+ "message" => "is too long (maximum is 16 characters)",
201
+ "reference" => "string"
202
+ }
203
+ ] )
204
+ end
205
+
206
+ it 'handles varying validation types via the alternative interface' do
207
+ m = RSpecModelErrorMappingTest.new( {
208
+ :boolean => true,
209
+ :date => Time.now,
210
+ :datetime => Time.now,
211
+ :decimal => 2.3,
212
+ :float => 2.3,
213
+ :integer => 42,
214
+ :string => "hello - this is far too long for the maximum field length",
215
+ :text => "hello",
216
+ :time => Time.now,
217
+ :array => [ 'hello' ]
218
+ } )
219
+
220
+ errors = m.platform_errors
221
+ expect( errors.errors ).to eq( [
222
+ {
223
+ "code" => "generic.invalid_string",
224
+ "message" => "is too long (maximum is 16 characters)",
225
+ "reference" => "string"
226
+ }
227
+ ] )
228
+ end
229
+
230
+ it 'handles arrays' do
231
+ m = RSpecModelErrorMappingTest.new( {
232
+ :boolean => true,
233
+ :date => Time.now,
234
+ :datetime => Time.now,
235
+ :decimal => 2.3,
236
+ :float => 2.3,
237
+ :integer => 42,
238
+ :string => 'hello',
239
+ :text => 'world',
240
+ :time => Time.now,
241
+ :array => []
242
+ } )
243
+
244
+ array_col = RSpecModelErrorMappingTest.columns_hash[ 'array' ]
245
+ expect( array_col ).to receive( :array ).once.and_return( true )
246
+
247
+ m.adds_errors_to?( @errors )
248
+
249
+ expect( @errors.errors ).to eq( [
250
+ {
251
+ "code" => "generic.invalid_array",
252
+ "message" => "can't be blank",
253
+ "reference" => "array"
254
+ }
255
+ ] )
256
+ end
257
+
258
+ it 'maps duplicates' do
259
+ m = valid_model
260
+
261
+ m.adds_errors_to?( @errors )
262
+ expect( @errors.errors ).to eq( [] )
263
+ m.save!
264
+
265
+ n = m.dup
266
+ n.adds_errors_to?( @errors )
267
+ expect( @errors.errors ).to eq( [
268
+ {
269
+ "code" => "generic.invalid_duplication",
270
+ "message" => "has already been taken",
271
+ "reference" => "integer"
272
+ }
273
+ ] )
274
+ end
275
+
276
+ it 'handles downgrade to generic error code' do
277
+
278
+ class LocalHackableErrors < Hoodoo::ErrorDescriptions
279
+ end
280
+
281
+ @errors = Hoodoo::Errors.new( Hoodoo::ErrorDescriptions.new )
282
+
283
+ # We have to hack to test this...
284
+ #
285
+ desc = @errors.descriptions.instance_variable_get( '@descriptions' )
286
+ desc.delete( 'generic.invalid_boolean' )
287
+ desc.delete( 'generic.invalid_date' )
288
+
289
+ m = RSpecModelErrorMappingTest.new( {
290
+ :datetime => Time.now,
291
+ :decimal => 2.3,
292
+ :float => 2.3,
293
+ :integer => 42,
294
+ :string => "hello",
295
+ :text => "hello",
296
+ :time => Time.now,
297
+ :array => [ 'hello' ]
298
+ } )
299
+
300
+ m.adds_errors_to?( @errors )
301
+
302
+ expect( @errors.errors ).to eq( [
303
+ {
304
+ "code" => "generic.invalid_parameters",
305
+ "message" => "can't be blank",
306
+ "reference" => "boolean"
307
+ },
308
+ {
309
+ "code" => "generic.invalid_parameters",
310
+ "message" => "can't be blank",
311
+ "reference" => "date"
312
+ }
313
+ ] )
314
+ end
315
+
316
+ it 'adds nothing if the model is valid' do
317
+ m = valid_model
318
+
319
+ expect( m.adds_errors_to?( @errors ) ).to eq( false )
320
+ expect( @errors.errors ).to eq( [] )
321
+
322
+ expect { m.save! }.to_not raise_error
323
+ end
324
+
325
+ it 'has-many associations are dereferenced' do
326
+ attrs = valid_model.attributes
327
+ attrs[ 'r_spec_model_associated_error_mapping_tests_attributes' ] = [
328
+ { :other_string => 'ok 1' },
329
+ { :other_string => 'ok 2' },
330
+ { :other_string => 'too long, so fails validation' }
331
+ ]
332
+
333
+ m = RSpecModelErrorMappingTest.new( attrs )
334
+
335
+ m.adds_errors_to?( @errors )
336
+
337
+ expect( @errors.errors ).to eq( [
338
+ {
339
+ "code" => "generic.invalid_string",
340
+ "message" => "is too long (maximum is 6 characters)",
341
+ "reference" => "r_spec_model_associated_error_mapping_tests.other_string"
342
+ }
343
+ ] )
344
+ end
345
+
346
+ it 'works with dot-separated non-attribute paths' do
347
+ m = valid_model
348
+ m.string = 'magic'
349
+
350
+ m.adds_errors_to?( @errors )
351
+
352
+ expect( @errors.errors ).to eq( [
353
+ {
354
+ "code" => "generic.invalid_parameters",
355
+ "message" => "is not a column",
356
+ "reference" => "this.thing"
357
+ }
358
+ ] )
359
+ end
360
+ end