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.
- checksums.yaml +7 -0
- data/bin/hoodoo +5 -0
- data/lib/hoodoo.rb +27 -0
- data/lib/hoodoo/active.rb +32 -0
- data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
- data/lib/hoodoo/active/active_record/base.rb +81 -0
- data/lib/hoodoo/active/active_record/creator.rb +134 -0
- data/lib/hoodoo/active/active_record/dated.rb +343 -0
- data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
- data/lib/hoodoo/active/active_record/finder.rb +606 -0
- data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
- data/lib/hoodoo/active/active_record/secure.rb +431 -0
- data/lib/hoodoo/active/active_record/support.rb +106 -0
- data/lib/hoodoo/active/active_record/translated.rb +87 -0
- data/lib/hoodoo/active/active_record/uuid.rb +80 -0
- data/lib/hoodoo/active/active_record/writer.rb +321 -0
- data/lib/hoodoo/client.rb +23 -0
- data/lib/hoodoo/client/augmented_array.rb +29 -0
- data/lib/hoodoo/client/augmented_base.rb +168 -0
- data/lib/hoodoo/client/augmented_hash.rb +23 -0
- data/lib/hoodoo/client/client.rb +354 -0
- data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
- data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
- data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
- data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
- data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
- data/lib/hoodoo/client/headers.rb +269 -0
- data/lib/hoodoo/communicators.rb +23 -0
- data/lib/hoodoo/communicators/fast.rb +44 -0
- data/lib/hoodoo/communicators/pool.rb +601 -0
- data/lib/hoodoo/communicators/slow.rb +84 -0
- data/lib/hoodoo/data.rb +51 -0
- data/lib/hoodoo/data/resources/caller.rb +39 -0
- data/lib/hoodoo/data/resources/errors.rb +28 -0
- data/lib/hoodoo/data/resources/log.rb +31 -0
- data/lib/hoodoo/data/resources/session.rb +26 -0
- data/lib/hoodoo/data/types/error_primitive.rb +27 -0
- data/lib/hoodoo/data/types/permissions.rb +40 -0
- data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
- data/lib/hoodoo/data/types/permissions_full.rb +28 -0
- data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
- data/lib/hoodoo/discovery.rb +20 -0
- data/lib/hoodoo/errors.rb +19 -0
- data/lib/hoodoo/errors/error_descriptions.rb +229 -0
- data/lib/hoodoo/errors/errors.rb +322 -0
- data/lib/hoodoo/generator.rb +139 -0
- data/lib/hoodoo/logger.rb +23 -0
- data/lib/hoodoo/logger/fast_writer.rb +27 -0
- data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
- data/lib/hoodoo/logger/logger.rb +387 -0
- data/lib/hoodoo/logger/slow_writer.rb +49 -0
- data/lib/hoodoo/logger/writer_mixin.rb +52 -0
- data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
- data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
- data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
- data/lib/hoodoo/middleware.rb +33 -0
- data/lib/hoodoo/presenters.rb +45 -0
- data/lib/hoodoo/presenters/base.rb +281 -0
- data/lib/hoodoo/presenters/base_dsl.rb +519 -0
- data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
- data/lib/hoodoo/presenters/embedding.rb +232 -0
- data/lib/hoodoo/presenters/types/array.rb +118 -0
- data/lib/hoodoo/presenters/types/boolean.rb +26 -0
- data/lib/hoodoo/presenters/types/date.rb +26 -0
- data/lib/hoodoo/presenters/types/date_time.rb +26 -0
- data/lib/hoodoo/presenters/types/decimal.rb +47 -0
- data/lib/hoodoo/presenters/types/enum.rb +55 -0
- data/lib/hoodoo/presenters/types/field.rb +158 -0
- data/lib/hoodoo/presenters/types/float.rb +26 -0
- data/lib/hoodoo/presenters/types/hash.rb +361 -0
- data/lib/hoodoo/presenters/types/integer.rb +26 -0
- data/lib/hoodoo/presenters/types/object.rb +117 -0
- data/lib/hoodoo/presenters/types/string.rb +53 -0
- data/lib/hoodoo/presenters/types/tags.rb +24 -0
- data/lib/hoodoo/presenters/types/text.rb +26 -0
- data/lib/hoodoo/presenters/types/uuid.rb +54 -0
- data/lib/hoodoo/services.rb +34 -0
- data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
- data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
- data/lib/hoodoo/services/discovery/discovery.rb +186 -0
- data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
- data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
- data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
- data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
- data/lib/hoodoo/services/middleware/interaction.rb +127 -0
- data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
- data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
- data/lib/hoodoo/services/services/context.rb +153 -0
- data/lib/hoodoo/services/services/implementation.rb +132 -0
- data/lib/hoodoo/services/services/interface.rb +934 -0
- data/lib/hoodoo/services/services/permissions.rb +250 -0
- data/lib/hoodoo/services/services/request.rb +189 -0
- data/lib/hoodoo/services/services/response.rb +316 -0
- data/lib/hoodoo/services/services/service.rb +141 -0
- data/lib/hoodoo/services/services/session.rb +729 -0
- data/lib/hoodoo/utilities.rb +12 -0
- data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
- data/lib/hoodoo/utilities/utilities.rb +380 -0
- data/lib/hoodoo/utilities/uuid.rb +44 -0
- data/lib/hoodoo/version.rb +17 -0
- data/spec/active/active_record/base_spec.rb +57 -0
- data/spec/active/active_record/creator_spec.rb +88 -0
- data/spec/active/active_record/dated_spec.rb +248 -0
- data/spec/active/active_record/error_mapping_spec.rb +360 -0
- data/spec/active/active_record/finder_spec.rb +744 -0
- data/spec/active/active_record/search_helper_spec.rb +384 -0
- data/spec/active/active_record/secure_spec.rb +435 -0
- data/spec/active/active_record/support_spec.rb +225 -0
- data/spec/active/active_record/translated_spec.rb +19 -0
- data/spec/active/active_record/uuid_spec.rb +72 -0
- data/spec/active/active_record/writer_spec.rb +272 -0
- data/spec/alchemy/alchemy-amq.rb +33 -0
- data/spec/client/augmented_array_spec.rb +15 -0
- data/spec/client/augmented_base_spec.rb +50 -0
- data/spec/client/augmented_hash_spec.rb +15 -0
- data/spec/client/client_spec.rb +955 -0
- data/spec/client/endpoint/endpoint_spec.rb +70 -0
- data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
- data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
- data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
- data/spec/client/headers_spec.rb +172 -0
- data/spec/communicators/fast_spec.rb +9 -0
- data/spec/communicators/pool_spec.rb +339 -0
- data/spec/communicators/slow_spec.rb +15 -0
- data/spec/data/resources/caller_spec.rb +156 -0
- data/spec/data/resources/errors_spec.rb +22 -0
- data/spec/data/resources/log_spec.rb +20 -0
- data/spec/data/resources/session_spec.rb +15 -0
- data/spec/data/types/error_primitive_spec.rb +15 -0
- data/spec/data/types/permissions_defaults_spec.rb +25 -0
- data/spec/data/types/permissions_full_spec.rb +44 -0
- data/spec/data/types/permissions_resources_spec.rb +34 -0
- data/spec/data/types/permissions_spec.rb +37 -0
- data/spec/errors/error_descriptions_spec.rb +98 -0
- data/spec/errors/errors_spec.rb +346 -0
- data/spec/integration/service_actions_spec.rb +112 -0
- data/spec/logger/fast_writer_spec.rb +18 -0
- data/spec/logger/logger_spec.rb +259 -0
- data/spec/logger/slow_writer_spec.rb +144 -0
- data/spec/logger/writers/file_writer_spec.rb +37 -0
- data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
- data/spec/logger/writers/stream_writer_spec.rb +38 -0
- data/spec/presenters/base_dsl_spec.rb +111 -0
- data/spec/presenters/base_spec.rb +871 -0
- data/spec/presenters/common_resource_fields_spec.rb +30 -0
- data/spec/presenters/embedding_spec.rb +87 -0
- data/spec/presenters/types/array_spec.rb +249 -0
- data/spec/presenters/types/boolean_spec.rb +51 -0
- data/spec/presenters/types/date_spec.rb +57 -0
- data/spec/presenters/types/date_time_spec.rb +59 -0
- data/spec/presenters/types/decimal_spec.rb +58 -0
- data/spec/presenters/types/enum_spec.rb +71 -0
- data/spec/presenters/types/field_spec.rb +77 -0
- data/spec/presenters/types/float_spec.rb +50 -0
- data/spec/presenters/types/hash_spec.rb +1069 -0
- data/spec/presenters/types/integer_spec.rb +50 -0
- data/spec/presenters/types/object_spec.rb +177 -0
- data/spec/presenters/types/string_spec.rb +65 -0
- data/spec/presenters/types/tags_spec.rb +56 -0
- data/spec/presenters/types/text_spec.rb +50 -0
- data/spec/presenters/types/uuid_spec.rb +46 -0
- data/spec/presenters/walk_spec.rb +198 -0
- data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
- data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
- data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
- data/spec/services/discovery/discovery_spec.rb +73 -0
- data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
- data/spec/services/discovery/results/for_http_spec.rb +37 -0
- data/spec/services/discovery/results/for_local_spec.rb +21 -0
- data/spec/services/discovery/results/for_remote_spec.rb +15 -0
- data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
- data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
- data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
- data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
- data/spec/services/middleware/middleware_cors_spec.rb +93 -0
- data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
- data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
- data/spec/services/middleware/middleware_logging_spec.rb +356 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
- data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
- data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
- data/spec/services/middleware/middleware_public_spec.rb +238 -0
- data/spec/services/middleware/middleware_spec.rb +1569 -0
- data/spec/services/middleware/string_inquirer_spec.rb +30 -0
- data/spec/services/services/application_spec.rb +74 -0
- data/spec/services/services/context_spec.rb +48 -0
- data/spec/services/services/implementation_spec.rb +45 -0
- data/spec/services/services/interface_spec.rb +262 -0
- data/spec/services/services/permissions_spec.rb +249 -0
- data/spec/services/services/request_spec.rb +95 -0
- data/spec/services/services/response_spec.rb +250 -0
- data/spec/services/services/session_spec.rb +432 -0
- data/spec/spec_helper.rb +298 -0
- data/spec/utilities/utilities_spec.rb +537 -0
- data/spec/utilities/uuid_spec.rb +20 -0
- metadata +615 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a8eb578694851746b49e396703f6c903f54ec246
|
4
|
+
data.tar.gz: c7af73f5ff6427953502ff131b5342d1e7993886
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7b67ac9d46b515be1e8d66a1ca41b4e16c2598ee3cf24a9e10199fc66d5daf1dfba74a7a437f75626463a98be1de8f673d180b22a561d460193c11b151cd15ce
|
7
|
+
data.tar.gz: c7b45c63ac2e9fd1588e127c449cece947f5aecb0c5cba0182c097b7381ff53aee94c2f1cc37025c7c56c0a0f1b1bdbab625da22a0fea77fbbdc463e77c31b82
|
data/bin/hoodoo
ADDED
data/lib/hoodoo.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: hoodoo.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Include all parts of Hoodoo.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 29-Jan-2015 (ADH): Added file documentation.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
# Module used as a namespace for all of Hoodoo's facilities.
|
11
|
+
#
|
12
|
+
module Hoodoo
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'hoodoo/utilities'
|
16
|
+
require 'hoodoo/communicators'
|
17
|
+
require 'hoodoo/logger'
|
18
|
+
require 'hoodoo/presenters'
|
19
|
+
require 'hoodoo/data'
|
20
|
+
require 'hoodoo/errors'
|
21
|
+
require 'hoodoo/active'
|
22
|
+
require 'hoodoo/client'
|
23
|
+
require 'hoodoo/discovery'
|
24
|
+
require 'hoodoo/services'
|
25
|
+
require 'hoodoo/middleware'
|
26
|
+
|
27
|
+
require 'hoodoo/version'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: active.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Include ActiveRecord-/ActiveModel-dependent optional
|
6
|
+
# extras.
|
7
|
+
# ----------------------------------------------------------------------
|
8
|
+
# 26-Jan-2015 (ADH): Split from top-level inclusion file.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
# Dependencies
|
12
|
+
|
13
|
+
require 'hoodoo/utilities'
|
14
|
+
require 'hoodoo/errors'
|
15
|
+
|
16
|
+
# ActiveRecord / ActiveModel extras
|
17
|
+
|
18
|
+
require 'hoodoo/active/active_model/uuid_validator'
|
19
|
+
|
20
|
+
require 'hoodoo/active/active_record/support'
|
21
|
+
require 'hoodoo/active/active_record/error_mapping'
|
22
|
+
|
23
|
+
require 'hoodoo/active/active_record/secure'
|
24
|
+
require 'hoodoo/active/active_record/dated'
|
25
|
+
require 'hoodoo/active/active_record/translated'
|
26
|
+
require 'hoodoo/active/active_record/finder'
|
27
|
+
|
28
|
+
require 'hoodoo/active/active_record/uuid'
|
29
|
+
require 'hoodoo/active/active_record/creator'
|
30
|
+
require 'hoodoo/active/active_record/writer'
|
31
|
+
|
32
|
+
require 'hoodoo/active/active_record/base'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: uuid_validator.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: UUID validator for models.
|
6
|
+
#
|
7
|
+
# ----------------------------------------------------------------------
|
8
|
+
# 26-Nov-2014 (RJS): Created.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
module Hoodoo
|
12
|
+
module ActiveRecord
|
13
|
+
begin
|
14
|
+
require 'active_model'
|
15
|
+
|
16
|
+
# Provides simple UUID validation via an ActiveModel::EachValidator.
|
17
|
+
# Uuid is not capitalised as ActiveModel's "magic" cannot find the
|
18
|
+
# validator if it is.
|
19
|
+
#
|
20
|
+
class ::UuidValidator < ::ActiveModel::EachValidator
|
21
|
+
|
22
|
+
# Any field this validator is applied to is considered valid if it is
|
23
|
+
# +nil+ or a valid UUID. In the case of UUIDs which should not be nil,
|
24
|
+
# a separate validation must be added.
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
#
|
28
|
+
# class SomeModel < ActiveRecord::Base
|
29
|
+
#
|
30
|
+
# validates :somefield, uuid: true
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
def validate_each( record, attribute, value )
|
34
|
+
|
35
|
+
unless value.nil? || Hoodoo::UUID.valid?( value )
|
36
|
+
record.errors[ attribute ] << ( options[ :message ] || 'is invalid' )
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: base.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Include all mixins.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 25-Nov-2014 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
module Hoodoo
|
11
|
+
module ActiveRecord
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'active_record'
|
15
|
+
|
16
|
+
# While individual ActiveRecord mixins can be included as and
|
17
|
+
# when needed, if you want the set of mixins, just define a model
|
18
|
+
# which subclasses from this Hoodoo::ActiveRecord::Base class
|
19
|
+
# instead of ActiveRecord::Base.
|
20
|
+
#
|
21
|
+
# This will include:
|
22
|
+
#
|
23
|
+
# * Hoodoo::ActiveRecord::Secure
|
24
|
+
# * Hoodoo::ActiveRecord::Dated
|
25
|
+
# * Hoodoo::ActiveRecord::Translated
|
26
|
+
# * Hoodoo::ActiveRecord::Finder
|
27
|
+
# * Hoodoo::ActiveRecord::UUID
|
28
|
+
# * Hoodoo::ActiveRecord::Creator
|
29
|
+
# * Hoodoo::ActiveRecord::Writer
|
30
|
+
# * Hoodoo::ActiveRecord::ErrorMapping
|
31
|
+
#
|
32
|
+
class Base < ::ActiveRecord::Base
|
33
|
+
|
34
|
+
# Reading data.
|
35
|
+
#
|
36
|
+
include Hoodoo::ActiveRecord::Secure
|
37
|
+
include Hoodoo::ActiveRecord::Dated
|
38
|
+
include Hoodoo::ActiveRecord::Translated
|
39
|
+
include Hoodoo::ActiveRecord::Finder
|
40
|
+
|
41
|
+
# Writing data.
|
42
|
+
#
|
43
|
+
include Hoodoo::ActiveRecord::UUID
|
44
|
+
include Hoodoo::ActiveRecord::Creator
|
45
|
+
include Hoodoo::ActiveRecord::Writer
|
46
|
+
|
47
|
+
# Other features.
|
48
|
+
#
|
49
|
+
include Hoodoo::ActiveRecord::ErrorMapping
|
50
|
+
|
51
|
+
# Tells ActiveRecord this is not a model that is persisted.
|
52
|
+
#
|
53
|
+
self.abstract_class = true
|
54
|
+
|
55
|
+
# Instantiates all the ActiveRecord mixins when this class is
|
56
|
+
# inherited.
|
57
|
+
#
|
58
|
+
# +model+:: The ActiveRecord::Base descendant that is including
|
59
|
+
# this module.
|
60
|
+
#
|
61
|
+
def self.inherited( model )
|
62
|
+
|
63
|
+
Hoodoo::ActiveRecord::Secure.instantiate( model )
|
64
|
+
Hoodoo::ActiveRecord::Dated.instantiate( model )
|
65
|
+
Hoodoo::ActiveRecord::Translated.instantiate( model )
|
66
|
+
Hoodoo::ActiveRecord::Finder.instantiate( model )
|
67
|
+
|
68
|
+
Hoodoo::ActiveRecord::UUID.instantiate( model )
|
69
|
+
Hoodoo::ActiveRecord::Creator.instantiate( model )
|
70
|
+
Hoodoo::ActiveRecord::Writer.instantiate( model )
|
71
|
+
|
72
|
+
super( model )
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
rescue LoadError
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: creator.rb
|
3
|
+
# (C):: Loyalty New Zealand 2015
|
4
|
+
#
|
5
|
+
# Purpose:: Support mixin for models subclassed from ActiveRecord::Base
|
6
|
+
# providing context-aware model instance creation, allowing
|
7
|
+
# service authors to auto-inherit related features from Hoodoo
|
8
|
+
# without changing their code.
|
9
|
+
# ----------------------------------------------------------------------
|
10
|
+
# 07-Dec-2015 (ADH): Created as a proper place for "new_in",
|
11
|
+
# which had historically and confusingly
|
12
|
+
# resided inside the Finder mixin.
|
13
|
+
########################################################################
|
14
|
+
|
15
|
+
module Hoodoo
|
16
|
+
|
17
|
+
# Support mixins for models subclassed from ActiveRecord::Base. See:
|
18
|
+
#
|
19
|
+
# * http://guides.rubyonrails.org/active_record_basics.html
|
20
|
+
#
|
21
|
+
module ActiveRecord
|
22
|
+
|
23
|
+
# Support mixin for models subclassed from ActiveRecord::Base providing
|
24
|
+
# context-aware model instance creation, allowing service authors to
|
25
|
+
# auto-inherit related features from Hoodoo without changing their code.
|
26
|
+
#
|
27
|
+
# It is _STRONGLY_ _RECOMMENDED_ that you use the likes of:
|
28
|
+
#
|
29
|
+
# * Hoodoo::ActiveRecord::Creator::ClassMethods::new_in
|
30
|
+
#
|
31
|
+
# ...to create model instances and participate "for free" in whatever
|
32
|
+
# plug-in ActiveRecord modules are mixed into the model classes, such as
|
33
|
+
# Hoodoo::ActiveRecord::Dated.
|
34
|
+
#
|
35
|
+
# See also:
|
36
|
+
#
|
37
|
+
# * http://guides.rubyonrails.org/active_record_basics.html
|
38
|
+
#
|
39
|
+
module Creator
|
40
|
+
|
41
|
+
# Instantiates this module when it is included:
|
42
|
+
#
|
43
|
+
# Example:
|
44
|
+
#
|
45
|
+
# class SomeModel < ActiveRecord::Base
|
46
|
+
# include Hoodoo::ActiveRecord::Creator
|
47
|
+
# # ...
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# +model+:: The ActiveRecord::Base descendant that is including
|
51
|
+
# this module.
|
52
|
+
#
|
53
|
+
def self.included( model )
|
54
|
+
instantiate( model ) unless model == Hoodoo::ActiveRecord::Base
|
55
|
+
super( model )
|
56
|
+
end
|
57
|
+
|
58
|
+
# When instantiated in an ActiveRecord::Base subclass, all of the
|
59
|
+
# Hoodoo::ActiveRecord::Dated::ClassMethods methods are defined as
|
60
|
+
# class methods on the including class.
|
61
|
+
#
|
62
|
+
# +model+:: The ActiveRecord::Base descendant that is including
|
63
|
+
# this module.
|
64
|
+
#
|
65
|
+
def self.instantiate( model )
|
66
|
+
model.extend( ClassMethods )
|
67
|
+
end
|
68
|
+
|
69
|
+
# Collection of class methods that get defined on an including class via
|
70
|
+
# Hoodoo::ActiveRecord::Creator::included.
|
71
|
+
#
|
72
|
+
module ClassMethods
|
73
|
+
|
74
|
+
# Create an instance of this model with knowledge of the wider request
|
75
|
+
# context. This may lead to important things like support of inbound
|
76
|
+
# "dated_from" values, depending upon the Hoodoo mixins included (or
|
77
|
+
# not) by this class - see Hoodoo::ActiveRecord::Dated.
|
78
|
+
#
|
79
|
+
# You use this exactly as you would for ActiveRecord::Core#new, but an
|
80
|
+
# additional, mandatory first parameter providing the request context
|
81
|
+
# must be supplied. For example, instead of this:
|
82
|
+
#
|
83
|
+
# instance = SomeActiveRecordSubclass.new( attrs )
|
84
|
+
#
|
85
|
+
# ...do this inside a resource implementation:
|
86
|
+
#
|
87
|
+
# instance = SomeActiveRecordSubclass.new_in( context, attrs )
|
88
|
+
#
|
89
|
+
# See also:
|
90
|
+
#
|
91
|
+
# * http://api.rubyonrails.org/classes/ActiveRecord/Base.html
|
92
|
+
#
|
93
|
+
# Parameters:
|
94
|
+
#
|
95
|
+
# +context+:: Hoodoo::Services::Context instance describing a call
|
96
|
+
# context. This is typically a value passed to one of
|
97
|
+
# the Hoodoo::Services::Implementation instance methods
|
98
|
+
# that a resource subclass implements.
|
99
|
+
#
|
100
|
+
# +attributes+:: Optional model attributes Hash, passed through to
|
101
|
+
# ActiveRecord::Core#new.
|
102
|
+
#
|
103
|
+
# &block:: Optional block for initialisation, passed through to
|
104
|
+
# ActiveRecord::Core#new.
|
105
|
+
#
|
106
|
+
# Returns a new model instance which may have context-derived values
|
107
|
+
# set for some attributes, in addition to anything set through the
|
108
|
+
# +attributes+ or <tt>&block</tt> parameters, if present.
|
109
|
+
#
|
110
|
+
# Note that context-dependent data is set _AFTER_ attribute or block
|
111
|
+
# based values, so takes precedence over anything you might set up
|
112
|
+
# using those parameters.
|
113
|
+
#
|
114
|
+
def new_in( context, attributes = nil, &block )
|
115
|
+
|
116
|
+
instance = self.new( attributes, &block )
|
117
|
+
|
118
|
+
# TODO: Refactor this to use the scope chain plugin approach in due
|
119
|
+
# course, but for now, pragmatic implementation does the only
|
120
|
+
# thing we currently need to do - set "created_at".
|
121
|
+
#
|
122
|
+
if self.include?( Hoodoo::ActiveRecord::Dated )
|
123
|
+
unless context.request.dated_from.nil?
|
124
|
+
instance.created_at = instance.updated_at = context.request.dated_from
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
return instance
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: dated.rb
|
3
|
+
# (C):: Loyalty New Zealand 2015
|
4
|
+
#
|
5
|
+
# Purpose:: Support mixin for models subclassed from ActiveRecord::Base
|
6
|
+
# providing as-per-API-standard dating support.
|
7
|
+
# ----------------------------------------------------------------------
|
8
|
+
# 14-Jul-2015 (ADH): Created.
|
9
|
+
# 21-Jul-2015 (RJS): Functionality implemented.
|
10
|
+
########################################################################
|
11
|
+
|
12
|
+
module Hoodoo
|
13
|
+
module ActiveRecord
|
14
|
+
|
15
|
+
# Support mixin for models subclassed from ActiveRecord::Base providing
|
16
|
+
# as-per-API-standard dating support.
|
17
|
+
#
|
18
|
+
# The facilities provided here are powerful but relatively complex, so
|
19
|
+
# please read through this documentation section in full to understand
|
20
|
+
# everything you need to do.
|
21
|
+
#
|
22
|
+
# == Overview
|
23
|
+
#
|
24
|
+
# This mixin adds finder methods to the model it is applied to (see
|
25
|
+
# Hoodoo::ActiveRecord::Dated::ClassMethods#dated and
|
26
|
+
# Hoodoo::ActiveRecord::Dated::ClassMethods#dated_at). These finders require
|
27
|
+
# two database tables in order to function correctly - the primary table
|
28
|
+
# (the model table) and a history table. When a record is updated it should
|
29
|
+
# be moved to the history table and a new record inserted with the new
|
30
|
+
# values. When a record is deleted it should be moved to the history
|
31
|
+
# table. This can be done manually with application code, or by things like
|
32
|
+
# SQL triggers (see later).
|
33
|
+
#
|
34
|
+
# Dating is only enabled if the including class explicitly calls the
|
35
|
+
# Hoodoo::ActiveRecord::Dated::ClassMethods#dating_enabled method.
|
36
|
+
#
|
37
|
+
# == Database table requirements
|
38
|
+
#
|
39
|
+
# In all related tables, all date-time values must be stored as UTC.
|
40
|
+
#
|
41
|
+
# The primary table _must_ have a unique column named +id+ and two
|
42
|
+
# timestamp columns named +updated_at+ and +created_at+ which both need
|
43
|
+
# to be set by the application code (the ActiveRecord +timestamps+ macro
|
44
|
+
# in a migration file defines appropriate columns).
|
45
|
+
#
|
46
|
+
# The history table requires the same columns as the primary table with two
|
47
|
+
# differences:
|
48
|
+
#
|
49
|
+
# 1. The history table's +id+ column must be populated with any unique
|
50
|
+
# value whilst the history table's +uuid+ column must be populated with
|
51
|
+
# the primary table's +id+ value.
|
52
|
+
#
|
53
|
+
# 2. The history table must have two additional columns, +effective_start+
|
54
|
+
# and +effective_end+. The +effective_start+ column determines when the
|
55
|
+
# history entry becomes effective (inclusive) whilst the +effective_end+
|
56
|
+
# determines when the history entry was effective to (exclusive). A record
|
57
|
+
# is considered to be effective at a particular time if that time is the
|
58
|
+
# same or after the +effective_start+ and before the +effective_end+.
|
59
|
+
#
|
60
|
+
# The +effective_start+ must be set to the +effective_end+ of the last
|
61
|
+
# record with same +uuid+, or to the +created_at+ of the record if there
|
62
|
+
# is no previous records with the same +uuid+.
|
63
|
+
#
|
64
|
+
# The +effective_end+ must be set to the current time (UTC) when
|
65
|
+
# deleting a record or to the updated record's +updated_at+ when updating
|
66
|
+
# a record.
|
67
|
+
#
|
68
|
+
# Additionally there are two constraints on the history table that must not
|
69
|
+
# be broken for the finder methods to function correctly:
|
70
|
+
#
|
71
|
+
# 1. When adding a record to the history table its +effective_end+ must be
|
72
|
+
# after all other records in the history table with the same +uuid+.
|
73
|
+
#
|
74
|
+
# 2. When inserting a new record to the primary table its +id+ must not
|
75
|
+
# exist in the history table.
|
76
|
+
#
|
77
|
+
# The history table name defaults to the name of the primary table
|
78
|
+
# concatenated with +_history_entries+. This can be overriden when calling
|
79
|
+
# Hoodoo::ActiveRecord::Dated::ClassMethods#dating_enabled.
|
80
|
+
#
|
81
|
+
# Example:
|
82
|
+
#
|
83
|
+
# class Post < ActiveRecord::Base
|
84
|
+
# include Hoodoo::ActiveRecord::Dated
|
85
|
+
# dating_enabled( history_table_name: 'historical_posts' )
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# == Migration assistance
|
89
|
+
#
|
90
|
+
# Compatible database migration generators are included in +service_shell+.
|
91
|
+
# These migrations create the history table and add database triggers
|
92
|
+
# (PostgreSQL specific) which will handle the creation of the appropriate
|
93
|
+
# history entry when a record is deleted or updated without breaking the
|
94
|
+
# history table constraints. See
|
95
|
+
# https://github.com/LoyaltyNZ/service_shell/blob/master/bin/generators/effective_date.rb
|
96
|
+
# for more information.
|
97
|
+
#
|
98
|
+
# == Model instance creation
|
99
|
+
#
|
100
|
+
# It is _VERY_ _IMPORTANT_ that you use
|
101
|
+
# Hoodoo::ActiveRecord::Creator::ClassMethods::new_in to create new
|
102
|
+
# resource instances when using Dating. You _could_ just manually read the
|
103
|
+
# `context.request.dated_from` value to ensure that an appropriate creation
|
104
|
+
# time is set; presently, `created_at` and `updated_at` are set from the
|
105
|
+
# `dated_from` value. However, using `new_in` for this isolates your code
|
106
|
+
# from any possible under-the-hood implementation changes therein and
|
107
|
+
# future-proof your code.
|
108
|
+
#
|
109
|
+
module Dated
|
110
|
+
|
111
|
+
# Instantiates this module when it is included:
|
112
|
+
#
|
113
|
+
# Example:
|
114
|
+
#
|
115
|
+
# class SomeModel < ActiveRecord::Base
|
116
|
+
# include Hoodoo::ActiveRecord::Dated
|
117
|
+
# # ...
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# +model+:: The ActiveRecord::Base descendant that is including
|
121
|
+
# this module.
|
122
|
+
#
|
123
|
+
def self.included( model )
|
124
|
+
model.class_attribute(
|
125
|
+
:nz_co_loyalty_hoodoo_dated_with,
|
126
|
+
{
|
127
|
+
:instance_predicate => false,
|
128
|
+
:instance_accessor => false
|
129
|
+
}
|
130
|
+
)
|
131
|
+
|
132
|
+
instantiate( model ) unless model == Hoodoo::ActiveRecord::Base
|
133
|
+
super( model )
|
134
|
+
end
|
135
|
+
|
136
|
+
# When instantiated in an ActiveRecord::Base subclass, all of the
|
137
|
+
# Hoodoo::ActiveRecord::Dated::ClassMethods methods are defined as
|
138
|
+
# class methods on the including class.
|
139
|
+
#
|
140
|
+
# +model+:: The ActiveRecord::Base descendant that is including
|
141
|
+
# this module.
|
142
|
+
#
|
143
|
+
def self.instantiate( model )
|
144
|
+
model.extend( ClassMethods )
|
145
|
+
end
|
146
|
+
|
147
|
+
# Forms a String containing the specified +model_klass+'s attribute names
|
148
|
+
# escaped and joined with commas.
|
149
|
+
#
|
150
|
+
# +model_klass+ Class which responds to .attribute_names
|
151
|
+
#
|
152
|
+
def self.sanitised_column_string( model_klass )
|
153
|
+
model_klass.attribute_names.map{ | c | ActiveRecord::Base.connection.quote_column_name( c ) }.join( ',' )
|
154
|
+
end
|
155
|
+
|
156
|
+
# Collection of class methods that get defined on an including class via
|
157
|
+
# Hoodoo::ActiveRecord::Dated::included.
|
158
|
+
#
|
159
|
+
module ClassMethods
|
160
|
+
|
161
|
+
# Activate historic dating for this model.
|
162
|
+
#
|
163
|
+
# See the module documentation for Hoodoo::ActiveRecord::Dated for
|
164
|
+
# full information on dating, table requirements, default table names
|
165
|
+
# and so forth.
|
166
|
+
#
|
167
|
+
# *Named* parameters are:
|
168
|
+
#
|
169
|
+
# +history_table_name+:: Optional String or Symbol name of the table
|
170
|
+
# which stores the history entries for this
|
171
|
+
# model. If omitted, defaults to the value
|
172
|
+
# described by the documentation for
|
173
|
+
# Hoodoo::ActiveRecord::Dated.
|
174
|
+
#
|
175
|
+
def dating_enabled( history_table_name: self.table_name + '_history_entries' )
|
176
|
+
|
177
|
+
# Define an anonymous model for the history entries.
|
178
|
+
|
179
|
+
history_klass = Class.new( ::ActiveRecord::Base ) do
|
180
|
+
self.primary_key = :id
|
181
|
+
self.table_name = history_table_name
|
182
|
+
end
|
183
|
+
|
184
|
+
# Record the anonymous model class in a namespaced class attribute
|
185
|
+
# for reference in the rest of the dating code via #dated_with().
|
186
|
+
|
187
|
+
self.nz_co_loyalty_hoodoo_dated_with = history_klass
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
# Return an ActiveRecord::Relation containing the model instances which
|
192
|
+
# are effective at +context.request.dated_at+. If this value is nil the
|
193
|
+
# current time in UTC is used.
|
194
|
+
#
|
195
|
+
# If historic dating hasn't been enabled via a call to #dating_enabled,
|
196
|
+
# then the default 'all' scope is returned instead.
|
197
|
+
#
|
198
|
+
# +context+:: Hoodoo::Services::Context instance describing a call
|
199
|
+
# context. This is typically a value passed to one of
|
200
|
+
# the Hoodoo::Services::Implementation instance methods
|
201
|
+
# that a resource subclass implements.
|
202
|
+
#
|
203
|
+
def dated( context )
|
204
|
+
date_time = context.request.dated_at || Time.now
|
205
|
+
return self.dated_at( date_time )
|
206
|
+
end
|
207
|
+
|
208
|
+
# Return an ActiveRecord::Relation scoping a query to include only model
|
209
|
+
# instances that are relevant/effective at the specified date_time.
|
210
|
+
#
|
211
|
+
# If historic dating hasn't been enabled via a call to #dating_enabled,
|
212
|
+
# then the default 'all' scope is returned instead.
|
213
|
+
#
|
214
|
+
# +date_time+:: (Optional) A Time or DateTime instance, or a String that
|
215
|
+
# can be converted to a DateTime instance, for which the
|
216
|
+
# "effective dated" scope is to be constructed.
|
217
|
+
#
|
218
|
+
def dated_at( date_time = Time.now )
|
219
|
+
|
220
|
+
dating_table_name = dated_with_table_name()
|
221
|
+
return all() if dating_table_name.nil? # "Model.all" -> returns anonymous scope
|
222
|
+
|
223
|
+
# Rationalise and convert the date time to UTC.
|
224
|
+
|
225
|
+
date_time = Hoodoo::Utilities.rationalise_datetime( date_time ).utc
|
226
|
+
|
227
|
+
# Create a string that specifies this model's attributes escaped and
|
228
|
+
# joined by commas for use in a SQL query.
|
229
|
+
|
230
|
+
formatted_model_attributes = Hoodoo::ActiveRecord::Dated.sanitised_column_string( self )
|
231
|
+
|
232
|
+
# Convert date_time to a String suitable for an SQL query.
|
233
|
+
|
234
|
+
string_date_time = sanitize( date_time )
|
235
|
+
|
236
|
+
# A query that combines historical and current records which are
|
237
|
+
# effective at the specified date time.
|
238
|
+
|
239
|
+
nested_query = %{
|
240
|
+
(
|
241
|
+
SELECT #{ formatted_model_attributes } FROM (
|
242
|
+
SELECT #{ formatted_model_attributes }, updated_at as effective_start, null AS effective_end
|
243
|
+
FROM #{ self.table_name }
|
244
|
+
|
245
|
+
UNION ALL
|
246
|
+
|
247
|
+
SELECT #{ self.dated_with_history_column_mapping }, effective_start, effective_end
|
248
|
+
FROM #{ dating_table_name }
|
249
|
+
) AS u
|
250
|
+
WHERE effective_start <= #{ string_date_time } AND (effective_end > #{ string_date_time } OR effective_end IS NULL)
|
251
|
+
) AS #{ self.table_name }
|
252
|
+
}
|
253
|
+
|
254
|
+
# Form a query which uses ActiveRecord to list a dated or current
|
255
|
+
# record.
|
256
|
+
|
257
|
+
return from( nested_query )
|
258
|
+
end
|
259
|
+
|
260
|
+
# Return an ActiveRecord::Relation scoping a query that would include
|
261
|
+
# all historical and current model instances.
|
262
|
+
#
|
263
|
+
# If historic dating hasn't been enabled via a call to #dating_enabled,
|
264
|
+
# then the default 'all' scope is returned instead.
|
265
|
+
#
|
266
|
+
def dated_historical_and_current
|
267
|
+
|
268
|
+
dating_table_name = dated_with_table_name()
|
269
|
+
return all() if dating_table_name.nil? # "Model.all" -> returns anonymous scope
|
270
|
+
|
271
|
+
# Create a string that specifies this model's attributes escaped and
|
272
|
+
# joined by commas for use in a SQL query.
|
273
|
+
|
274
|
+
formatted_model_attributes = Hoodoo::ActiveRecord::Dated.sanitised_column_string( self )
|
275
|
+
|
276
|
+
# A query that combines historical and current records.
|
277
|
+
|
278
|
+
nested_query = %{
|
279
|
+
(
|
280
|
+
SELECT #{ formatted_model_attributes }
|
281
|
+
FROM #{ self.table_name }
|
282
|
+
|
283
|
+
UNION ALL
|
284
|
+
|
285
|
+
SELECT #{ self.dated_with_history_column_mapping }
|
286
|
+
FROM #{ dating_table_name }
|
287
|
+
) AS #{ self.table_name }
|
288
|
+
}
|
289
|
+
|
290
|
+
# Form a query which uses ActiveRecord to list current and dated
|
291
|
+
# records.
|
292
|
+
|
293
|
+
return from( nested_query )
|
294
|
+
end
|
295
|
+
|
296
|
+
# Returns the anonymous ActiveRecord::Base instance used for this
|
297
|
+
# model's history entries, or +nil+ if historic dating has not been
|
298
|
+
# enabled via a prior call to #dating_enabled.
|
299
|
+
#
|
300
|
+
def dated_with
|
301
|
+
return self.nz_co_loyalty_hoodoo_dated_with
|
302
|
+
end
|
303
|
+
|
304
|
+
# Get the symbolised name of the history table for model. This defaults
|
305
|
+
# to the name of the model's table concatenated with +_history_entries+.
|
306
|
+
# If the table name is +:posts+, the history table would be
|
307
|
+
# +:posts_history_entries+.
|
308
|
+
#
|
309
|
+
# If historic dating hasn't been enabled via a call to #dating_enabled,
|
310
|
+
# returns +nil+.
|
311
|
+
#
|
312
|
+
def dated_with_table_name
|
313
|
+
instance = self.dated_with()
|
314
|
+
instance.nil? ? nil : instance.table_name
|
315
|
+
end
|
316
|
+
|
317
|
+
protected
|
318
|
+
|
319
|
+
# Forms and returns string which maps history table column names to the
|
320
|
+
# primary table column names for use in SQL queries.
|
321
|
+
#
|
322
|
+
def dated_with_history_column_mapping
|
323
|
+
desired_attributes = self.attribute_names.dup
|
324
|
+
|
325
|
+
# Locate the primary key field, which must be called "id".
|
326
|
+
|
327
|
+
primary_key_index = desired_attributes.index( 'id' )
|
328
|
+
|
329
|
+
# Sanitise the attribute names.
|
330
|
+
|
331
|
+
desired_attributes.map!{ | c | ActiveRecord::Base.connection.quote_column_name( c ) }
|
332
|
+
|
333
|
+
# Map the primary key.
|
334
|
+
|
335
|
+
desired_attributes[ primary_key_index ] = 'uuid as ' << desired_attributes[ primary_key_index ]
|
336
|
+
|
337
|
+
return desired_attributes.join( ',' )
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|