all_futures 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -1
  3. data/Gemfile.lock +297 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +59 -77
  6. data/Rakefile +13 -1
  7. data/SUMMARY.md +22 -2
  8. data/bin/console +8 -0
  9. data/bin/loc +0 -0
  10. data/bin/setup +0 -0
  11. data/bin/standardize +0 -0
  12. data/lib/all_futures/association.rb +42 -0
  13. data/lib/all_futures/association_index.rb +69 -0
  14. data/lib/all_futures/attributes.rb +28 -0
  15. data/lib/all_futures/base.rb +66 -0
  16. data/lib/all_futures/callbacks.rb +71 -0
  17. data/lib/all_futures/configuration.rb +9 -0
  18. data/lib/all_futures/dirty.rb +116 -0
  19. data/lib/all_futures/embeds.rb +40 -0
  20. data/lib/all_futures/errors.rb +37 -0
  21. data/lib/all_futures/finder.rb +214 -0
  22. data/lib/all_futures/locale/en.yml +49 -0
  23. data/lib/all_futures/persistence.rb +595 -0
  24. data/lib/all_futures/presenter.rb +35 -0
  25. data/lib/all_futures/railtie.rb +26 -0
  26. data/lib/all_futures/translation.rb +22 -0
  27. data/lib/all_futures/validations.rb +10 -0
  28. data/lib/all_futures/version.rb +3 -1
  29. data/lib/all_futures/versions.rb +80 -0
  30. data/lib/all_futures.rb +24 -1
  31. data/lib/vendor/ACTIVE_ENTITY_MIT-LICENSE +42 -0
  32. data/lib/vendor/README.md +8 -0
  33. data/lib/vendor/active_entity/aggregations.rb +277 -0
  34. data/lib/vendor/active_entity/associations/embeds/association.rb +134 -0
  35. data/lib/vendor/active_entity/associations/embeds/builder/association.rb +105 -0
  36. data/lib/vendor/active_entity/associations/embeds/builder/collection_association.rb +71 -0
  37. data/lib/vendor/active_entity/associations/embeds/builder/embedded_in.rb +38 -0
  38. data/lib/vendor/active_entity/associations/embeds/builder/embeds_many.rb +13 -0
  39. data/lib/vendor/active_entity/associations/embeds/builder/embeds_one.rb +16 -0
  40. data/lib/vendor/active_entity/associations/embeds/builder/singular_association.rb +28 -0
  41. data/lib/vendor/active_entity/associations/embeds/collection_association.rb +209 -0
  42. data/lib/vendor/active_entity/associations/embeds/collection_proxy.rb +310 -0
  43. data/lib/vendor/active_entity/associations/embeds/embedded_in_association.rb +31 -0
  44. data/lib/vendor/active_entity/associations/embeds/embeds_many_association.rb +15 -0
  45. data/lib/vendor/active_entity/associations/embeds/embeds_one_association.rb +26 -0
  46. data/lib/vendor/active_entity/associations/embeds/singular_association.rb +35 -0
  47. data/lib/vendor/active_entity/associations.rb +168 -0
  48. data/lib/vendor/active_entity/attribute_assignment.rb +87 -0
  49. data/lib/vendor/active_entity/attribute_decorators.rb +90 -0
  50. data/lib/vendor/active_entity/attribute_methods/before_type_cast.rb +78 -0
  51. data/lib/vendor/active_entity/attribute_methods/dirty.rb +180 -0
  52. data/lib/vendor/active_entity/attribute_methods/primary_key.rb +96 -0
  53. data/lib/vendor/active_entity/attribute_methods/query.rb +38 -0
  54. data/lib/vendor/active_entity/attribute_methods/read.rb +44 -0
  55. data/lib/vendor/active_entity/attribute_methods/serialization.rb +90 -0
  56. data/lib/vendor/active_entity/attribute_methods/time_zone_conversion.rb +93 -0
  57. data/lib/vendor/active_entity/attribute_methods/write.rb +54 -0
  58. data/lib/vendor/active_entity/attribute_methods.rb +833 -0
  59. data/lib/vendor/active_entity/attributes.rb +240 -0
  60. data/lib/vendor/active_entity/base.rb +293 -0
  61. data/lib/vendor/active_entity/callbacks.rb +21 -0
  62. data/lib/vendor/active_entity/coders/json.rb +15 -0
  63. data/lib/vendor/active_entity/coders/yaml_column.rb +50 -0
  64. data/lib/vendor/active_entity/core.rb +348 -0
  65. data/lib/vendor/active_entity/enum.rb +260 -0
  66. data/lib/vendor/active_entity/errors.rb +69 -0
  67. data/lib/vendor/active_entity/gem_version.rb +17 -0
  68. data/lib/vendor/active_entity/inheritance.rb +176 -0
  69. data/lib/vendor/active_entity/integration.rb +78 -0
  70. data/lib/vendor/active_entity/locale/en.yml +45 -0
  71. data/lib/vendor/active_entity/model_schema.rb +118 -0
  72. data/lib/vendor/active_entity/nested_attributes.rb +554 -0
  73. data/lib/vendor/active_entity/persistence.rb +20 -0
  74. data/lib/vendor/active_entity/railtie.rb +76 -0
  75. data/lib/vendor/active_entity/readonly_attributes.rb +55 -0
  76. data/lib/vendor/active_entity/reflection.rb +447 -0
  77. data/lib/vendor/active_entity/serialization.rb +28 -0
  78. data/lib/vendor/active_entity/store.rb +291 -0
  79. data/lib/vendor/active_entity/translation.rb +24 -0
  80. data/lib/vendor/active_entity/type/date.rb +9 -0
  81. data/lib/vendor/active_entity/type/date_time.rb +9 -0
  82. data/lib/vendor/active_entity/type/internal/timezone.rb +17 -0
  83. data/lib/vendor/active_entity/type/json.rb +30 -0
  84. data/lib/vendor/active_entity/type/modifiers/array.rb +72 -0
  85. data/lib/vendor/active_entity/type/modifiers/array_without_blank.rb +19 -0
  86. data/lib/vendor/active_entity/type/registry.rb +112 -0
  87. data/lib/vendor/active_entity/type/serialized.rb +71 -0
  88. data/lib/vendor/active_entity/type/text.rb +11 -0
  89. data/lib/vendor/active_entity/type/time.rb +21 -0
  90. data/lib/vendor/active_entity/type/unsigned_integer.rb +17 -0
  91. data/lib/vendor/active_entity/type.rb +75 -0
  92. data/lib/vendor/active_entity/validate_embeds_association.rb +300 -0
  93. data/lib/vendor/active_entity/validations/absence.rb +25 -0
  94. data/lib/vendor/active_entity/validations/associated.rb +60 -0
  95. data/lib/vendor/active_entity/validations/length.rb +26 -0
  96. data/lib/vendor/active_entity/validations/presence.rb +68 -0
  97. data/lib/vendor/active_entity/validations/subset.rb +76 -0
  98. data/lib/vendor/active_entity/validations/uniqueness_in_embeds.rb +91 -0
  99. data/lib/vendor/active_entity/validations/uniqueness_on_active_record.rb +234 -0
  100. data/lib/vendor/active_entity/validations.rb +47 -0
  101. data/lib/vendor/active_entity/version.rb +10 -0
  102. data/lib/vendor/active_entity.rb +99 -0
  103. data/lib/vendor/core_ext/array_without_blank.rb +48 -0
  104. metadata +172 -31
  105. data/lib/all_futures/all_futures.rb +0 -28
  106. data/lib/all_futures/config.rb +0 -14
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+
5
+ module ActiveEntity
6
+ # Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
7
+ # It's like a simple key/value store baked into your record when you don't care about being able to
8
+ # query that store outside the context of a single record.
9
+ #
10
+ # You can then declare accessors to this store that are then accessible just like any other attribute
11
+ # of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
12
+ # already built around just accessing attributes on the model.
13
+ #
14
+ # Every accessor comes with dirty tracking methods (+key_changed?+, +key_was+ and +key_change+) and
15
+ # methods to access the changes made during the last save (+saved_change_to_key?+, +saved_change_to_key+ and
16
+ # +key_before_last_save+).
17
+ #
18
+ # NOTE: There is no +key_will_change!+ method for accessors, use +store_will_change!+ instead.
19
+ #
20
+ # Make sure that you declare the database column used for the serialized store as a text, so there's
21
+ # plenty of room.
22
+ #
23
+ # You can set custom coder to encode/decode your serialized attributes to/from different formats.
24
+ # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
25
+ #
26
+ # NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, or MySQL 5.7+
27
+ # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
28
+ # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
29
+ # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
30
+ # using a symbol.
31
+ #
32
+ # NOTE: The default validations with the exception of +uniqueness+ will work.
33
+ # For example, if you want to check for +uniqueness+ with +hstore+ you will
34
+ # need to use a custom validation to handle it.
35
+ #
36
+ # Examples:
37
+ #
38
+ # class User < ActiveEntity::Base
39
+ # store :settings, accessors: [ :color, :homepage ], coder: JSON
40
+ # store :parent, accessors: [ :name ], coder: JSON, prefix: true
41
+ # store :spouse, accessors: [ :name ], coder: JSON, prefix: :partner
42
+ # store :settings, accessors: [ :two_factor_auth ], suffix: true
43
+ # store :settings, accessors: [ :login_retry ], suffix: :config
44
+ # end
45
+ #
46
+ # u = User.new(color: 'black', homepage: '37signals.com', parent_name: 'Mary', partner_name: 'Lily')
47
+ # u.color # Accessor stored attribute
48
+ # u.parent_name # Accessor stored attribute with prefix
49
+ # u.partner_name # Accessor stored attribute with custom prefix
50
+ # u.two_factor_auth_settings # Accessor stored attribute with suffix
51
+ # u.login_retry_config # Accessor stored attribute with custom suffix
52
+ # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
53
+ #
54
+ # # There is no difference between strings and symbols for accessing custom attributes
55
+ # u.settings[:country] # => 'Denmark'
56
+ # u.settings['country'] # => 'Denmark'
57
+ #
58
+ # # Dirty tracking
59
+ # u.color = 'green'
60
+ # u.color_changed? # => true
61
+ # u.color_was # => 'black'
62
+ # u.color_change # => ['black', 'red']
63
+ #
64
+ # # Add additional accessors to an existing store through store_accessor
65
+ # class SuperUser < User
66
+ # store_accessor :settings, :privileges, :servants
67
+ # store_accessor :parent, :birthday, prefix: true
68
+ # store_accessor :settings, :secret_question, suffix: :config
69
+ # end
70
+ #
71
+ # The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
72
+ #
73
+ # User.stored_attributes[:settings] # [:color, :homepage, :two_factor_auth, :login_retry]
74
+ #
75
+ # == Overwriting default accessors
76
+ #
77
+ # All stored values are automatically available through accessors on the Active Entity
78
+ # object, but sometimes you want to specialize this behavior. This can be done by overwriting
79
+ # the default accessors (using the same name as the attribute) and calling <tt>super</tt>
80
+ # to actually change things.
81
+ #
82
+ # class Song < ActiveEntity::Base
83
+ # # Uses a stored integer to hold the volume adjustment of the song
84
+ # store :settings, accessors: [:volume_adjustment]
85
+ #
86
+ # def volume_adjustment=(decibels)
87
+ # super(decibels.to_i)
88
+ # end
89
+ #
90
+ # def volume_adjustment
91
+ # super.to_i
92
+ # end
93
+ # end
94
+ module Store
95
+ extend ActiveSupport::Concern
96
+
97
+ included do
98
+ class << self
99
+ attr_accessor :local_stored_attributes
100
+ end
101
+ end
102
+
103
+ module ClassMethods
104
+ def store(store_attribute, options = {})
105
+ serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
106
+ store_accessor(store_attribute, options[:accessors], **options.slice(:prefix, :suffix)) if options.has_key? :accessors
107
+ end
108
+
109
+ def store_accessor(store_attribute, *keys, prefix: nil, suffix: nil)
110
+ keys = keys.flatten
111
+
112
+ accessor_prefix =
113
+ case prefix
114
+ when String, Symbol
115
+ "#{prefix}_"
116
+ when TrueClass
117
+ "#{store_attribute}_"
118
+ else
119
+ ""
120
+ end
121
+ accessor_suffix =
122
+ case suffix
123
+ when String, Symbol
124
+ "_#{suffix}"
125
+ when TrueClass
126
+ "_#{store_attribute}"
127
+ else
128
+ ""
129
+ end
130
+
131
+ _store_accessors_module.module_eval do
132
+ keys.each do |key|
133
+ accessor_key = "#{accessor_prefix}#{key}#{accessor_suffix}"
134
+
135
+ define_method("#{accessor_key}=") do |value|
136
+ write_store_attribute(store_attribute, key, value)
137
+ end
138
+
139
+ define_method(accessor_key) do
140
+ read_store_attribute(store_attribute, key)
141
+ end
142
+
143
+ define_method("#{accessor_key}_changed?") do
144
+ return false unless attribute_changed?(store_attribute)
145
+ prev_store, new_store = changes[store_attribute]
146
+ prev_store&.dig(key) != new_store&.dig(key)
147
+ end
148
+
149
+ define_method("#{accessor_key}_change") do
150
+ return unless attribute_changed?(store_attribute)
151
+ prev_store, new_store = changes[store_attribute]
152
+ [prev_store&.dig(key), new_store&.dig(key)]
153
+ end
154
+
155
+ define_method("#{accessor_key}_was") do
156
+ return unless attribute_changed?(store_attribute)
157
+ prev_store, _new_store = changes[store_attribute]
158
+ prev_store&.dig(key)
159
+ end
160
+
161
+ define_method("saved_change_to_#{accessor_key}?") do
162
+ return false unless saved_change_to_attribute?(store_attribute)
163
+ prev_store, new_store = saved_change_to_attribute(store_attribute)
164
+ prev_store&.dig(key) != new_store&.dig(key)
165
+ end
166
+
167
+ define_method("saved_change_to_#{accessor_key}") do
168
+ return unless saved_change_to_attribute?(store_attribute)
169
+ prev_store, new_store = saved_change_to_attribute(store_attribute)
170
+ [prev_store&.dig(key), new_store&.dig(key)]
171
+ end
172
+
173
+ define_method("#{accessor_key}_before_last_save") do
174
+ return unless saved_change_to_attribute?(store_attribute)
175
+ prev_store, _new_store = saved_change_to_attribute(store_attribute)
176
+ prev_store&.dig(key)
177
+ end
178
+ end
179
+ end
180
+
181
+ # assign new store attribute and create new hash to ensure that each class in the hierarchy
182
+ # has its own hash of stored attributes.
183
+ self.local_stored_attributes ||= {}
184
+ self.local_stored_attributes[store_attribute] ||= []
185
+ self.local_stored_attributes[store_attribute] |= keys
186
+ end
187
+
188
+ def _store_accessors_module # :nodoc:
189
+ @_store_accessors_module ||= begin
190
+ mod = Module.new
191
+ include mod
192
+ mod
193
+ end
194
+ end
195
+
196
+ def stored_attributes
197
+ parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
198
+ if local_stored_attributes
199
+ parent.merge!(local_stored_attributes) { |k, a, b| a | b }
200
+ end
201
+ parent
202
+ end
203
+ end
204
+
205
+ private
206
+
207
+ def read_store_attribute(store_attribute, key) # :doc:
208
+ accessor = store_accessor_for(store_attribute)
209
+ accessor.read(self, store_attribute, key)
210
+ end
211
+
212
+ def write_store_attribute(store_attribute, key, value) # :doc:
213
+ accessor = store_accessor_for(store_attribute)
214
+ accessor.write(self, store_attribute, key, value)
215
+ end
216
+
217
+ def store_accessor_for(store_attribute)
218
+ type_for_attribute(store_attribute).accessor
219
+ end
220
+
221
+ class HashAccessor # :nodoc:
222
+ def self.read(object, attribute, key)
223
+ prepare(object, attribute)
224
+ object.public_send(attribute)[key]
225
+ end
226
+
227
+ def self.write(object, attribute, key, value)
228
+ prepare(object, attribute)
229
+ if value != read(object, attribute, key)
230
+ object.public_send :"#{attribute}_will_change!"
231
+ object.public_send(attribute)[key] = value
232
+ end
233
+ end
234
+
235
+ def self.prepare(object, attribute)
236
+ object.public_send :"#{attribute}=", {} unless object.send(attribute)
237
+ end
238
+ end
239
+
240
+ class StringKeyedHashAccessor < HashAccessor # :nodoc:
241
+ def self.read(object, attribute, key)
242
+ super object, attribute, key.to_s
243
+ end
244
+
245
+ def self.write(object, attribute, key, value)
246
+ super object, attribute, key.to_s, value
247
+ end
248
+ end
249
+
250
+ class IndifferentHashAccessor < ActiveEntity::Store::HashAccessor # :nodoc:
251
+ def self.prepare(object, store_attribute)
252
+ attribute = object.send(store_attribute)
253
+ unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
254
+ attribute = IndifferentCoder.as_indifferent_hash(attribute)
255
+ object.send :"#{store_attribute}=", attribute
256
+ end
257
+ attribute
258
+ end
259
+ end
260
+
261
+ class IndifferentCoder # :nodoc:
262
+ def initialize(attr_name, coder_or_class_name)
263
+ @coder =
264
+ if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
265
+ coder_or_class_name
266
+ else
267
+ ActiveEntity::Coders::YAMLColumn.new(attr_name, coder_or_class_name || Object)
268
+ end
269
+ end
270
+
271
+ def dump(obj)
272
+ @coder.dump self.class.as_indifferent_hash(obj)
273
+ end
274
+
275
+ def load(yaml)
276
+ self.class.as_indifferent_hash(@coder.load(yaml || ""))
277
+ end
278
+
279
+ def self.as_indifferent_hash(obj)
280
+ case obj
281
+ when ActiveSupport::HashWithIndifferentAccess
282
+ obj
283
+ when Hash
284
+ obj.with_indifferent_access
285
+ else
286
+ ActiveSupport::HashWithIndifferentAccess.new
287
+ end
288
+ end
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Translation
5
+ include ActiveModel::Translation
6
+
7
+ # Set the lookup ancestors for ActiveModel.
8
+ def lookup_ancestors #:nodoc:
9
+ klass = self
10
+ classes = [klass]
11
+ return classes if klass == ActiveEntity::Base
12
+
13
+ while !klass.base_class?
14
+ classes << klass = klass.superclass
15
+ end
16
+ classes
17
+ end
18
+
19
+ # Set the i18n scope to overwrite ActiveModel.
20
+ def i18n_scope #:nodoc:
21
+ :active_entity
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ class Date < ActiveModel::Type::Date
6
+ include Internal::Timezone
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ class DateTime < ActiveModel::Type::DateTime
6
+ include Internal::Timezone
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ module Internal
6
+ module Timezone
7
+ def is_utc?
8
+ ActiveEntity::Base.default_timezone == :utc
9
+ end
10
+
11
+ def default_timezone
12
+ ActiveEntity::Base.default_timezone
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ class Json < ActiveModel::Type::Value
6
+ include ActiveModel::Type::Helpers::Mutable
7
+
8
+ def type
9
+ :json
10
+ end
11
+
12
+ def deserialize(value)
13
+ return value unless value.is_a?(::String)
14
+ ActiveSupport::JSON.decode(value) rescue nil
15
+ end
16
+
17
+ def serialize(value)
18
+ ActiveSupport::JSON.encode(value) unless value.nil?
19
+ end
20
+
21
+ def changed_in_place?(raw_old_value, new_value)
22
+ deserialize(raw_old_value) != new_value
23
+ end
24
+
25
+ def accessor
26
+ ActiveEntity::Store::StringKeyedHashAccessor
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ module Modifiers
6
+ class Array < ActiveModel::Type::Value # :nodoc:
7
+ include ActiveModel::Type::Helpers::Mutable
8
+
9
+ attr_reader :subtype, :delimiter
10
+ delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :subtype
11
+
12
+ def initialize(subtype, delimiter = ",")
13
+ @subtype = subtype
14
+ @delimiter = delimiter
15
+ end
16
+
17
+ def deserialize(value)
18
+ case value
19
+ when ::String
20
+ type_cast_array(value.split(@delimiter), :deserialize)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def cast(value)
27
+ if value.is_a?(::String)
28
+ value = value.split(@delimiter)
29
+ end
30
+ type_cast_array(value, :cast)
31
+ end
32
+
33
+ def serialize(value)
34
+ if value.is_a?(::Array)
35
+ casted_values = type_cast_array(value, :serialize)
36
+ casted_values.join(@delimiter)
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ def ==(other)
43
+ other.is_a?(Array) &&
44
+ subtype == other.subtype &&
45
+ delimiter == other.delimiter
46
+ end
47
+
48
+ def map(value, &block)
49
+ value.map(&block)
50
+ end
51
+
52
+ def changed_in_place?(raw_old_value, new_value)
53
+ deserialize(raw_old_value) != new_value
54
+ end
55
+
56
+ def force_equality?(value)
57
+ value.is_a?(::Array)
58
+ end
59
+
60
+ private
61
+
62
+ def type_cast_array(value, method)
63
+ if value.is_a?(::Array)
64
+ value.map { |item| type_cast_array(item, method) }
65
+ else
66
+ @subtype.public_send(method, value)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ module Modifiers
6
+ class ArrayWithoutBlank < Array # :nodoc:
7
+ private
8
+
9
+ def type_cast_array(value, method)
10
+ if value.is_a?(::Array)
11
+ ::ArrayWithoutBlank.new value.map { |item| type_cast_array(item, method) }
12
+ else
13
+ @subtype.public_send(method, value)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ # :stopdoc:
5
+ module Type
6
+ class Registry # :nodoc:
7
+ def initialize
8
+ @registrations = []
9
+ end
10
+
11
+ def initialize_copy(_other)
12
+ @registrations = @registrations.dup
13
+ end
14
+
15
+ def add_modifier(options, klass, **_args)
16
+ registrations << DecorationRegistration.new(options, klass)
17
+ end
18
+
19
+ def register(type_name, klass = nil, **options, &block)
20
+ unless block_given?
21
+ block = proc { |_, *args| klass.new(*args) }
22
+ block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
23
+ end
24
+ registrations << Registration.new(type_name, block, **options)
25
+ end
26
+
27
+ def lookup(symbol, *args, **kwargs)
28
+ registration = find_registration(symbol, *args, **kwargs)
29
+
30
+ if registration
31
+ registration.call(self, symbol, *args, **kwargs)
32
+ else
33
+ raise ArgumentError, "Unknown type #{symbol.inspect}"
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :registrations
40
+
41
+ def find_registration(symbol, *args, **kwargs)
42
+ registrations
43
+ .select { |registration| registration.matches?(symbol, *args, **kwargs) }
44
+ .max
45
+ end
46
+ end
47
+
48
+ class Registration # :nodoc:
49
+ def initialize(name, block, override: nil)
50
+ @name = name
51
+ @block = block
52
+ @override = override
53
+ end
54
+
55
+ def call(_registry, *args, **kwargs)
56
+ if kwargs.any? # https://bugs.ruby-lang.org/issues/10856
57
+ block.call(*args, **kwargs)
58
+ else
59
+ block.call(*args)
60
+ end
61
+ end
62
+
63
+ def matches?(type_name, *args, **kwargs)
64
+ type_name == name
65
+ end
66
+
67
+ def <=>(other)
68
+ priority <=> other.priority
69
+ end
70
+
71
+ protected
72
+
73
+ attr_reader :name, :block, :override
74
+
75
+ def priority
76
+ override ? 1 : 0
77
+ end
78
+ end
79
+
80
+ class DecorationRegistration < Registration # :nodoc:
81
+ def initialize(options, klass, **)
82
+ @options = options
83
+ @klass = klass
84
+ end
85
+
86
+ def call(registry, *args, **kwargs)
87
+ subtype = registry.lookup(*args, **kwargs.except(*options.keys))
88
+ klass.new(subtype)
89
+ end
90
+
91
+ def matches?(*args, **kwargs)
92
+ matches_options?(**kwargs)
93
+ end
94
+
95
+ def priority
96
+ super | 4
97
+ end
98
+
99
+ private
100
+
101
+ attr_reader :options, :klass
102
+
103
+ def matches_options?(**kwargs)
104
+ options.all? do |key, value|
105
+ kwargs[key] == value
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ # :startdoc:
112
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ class Serialized < DelegateClass(ActiveModel::Type::Value) # :nodoc:
6
+ undef to_yaml if method_defined?(:to_yaml)
7
+
8
+ include ActiveModel::Type::Helpers::Mutable
9
+
10
+ attr_reader :subtype, :coder
11
+
12
+ def initialize(subtype, coder)
13
+ @subtype = subtype
14
+ @coder = coder
15
+ super(subtype)
16
+ end
17
+
18
+ def deserialize(value)
19
+ if default_value?(value)
20
+ value
21
+ else
22
+ coder.load(super)
23
+ end
24
+ end
25
+
26
+ def serialize(value)
27
+ return if value.nil?
28
+ unless default_value?(value)
29
+ super coder.dump(value)
30
+ end
31
+ end
32
+
33
+ def inspect
34
+ Kernel.instance_method(:inspect).bind(self).call
35
+ end
36
+
37
+ def changed_in_place?(raw_old_value, value)
38
+ return false if value.nil?
39
+ raw_new_value = encoded(value)
40
+ raw_old_value.nil? != raw_new_value.nil? ||
41
+ subtype.changed_in_place?(raw_old_value, raw_new_value)
42
+ end
43
+
44
+ def accessor
45
+ ActiveEntity::Store::IndifferentHashAccessor
46
+ end
47
+
48
+ def assert_valid_value(value)
49
+ if coder.respond_to?(:assert_valid_value)
50
+ coder.assert_valid_value(value, action: "serialize")
51
+ end
52
+ end
53
+
54
+ def force_equality?(value)
55
+ coder.respond_to?(:object_class) && value.is_a?(coder.object_class)
56
+ end
57
+
58
+ private
59
+
60
+ def default_value?(value)
61
+ value == coder.load(nil)
62
+ end
63
+
64
+ def encoded(value)
65
+ unless default_value?(value)
66
+ coder.dump(value)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ class Text < ActiveModel::Type::String # :nodoc:
6
+ def type
7
+ :text
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module Type
5
+ class Time < ActiveModel::Type::Time
6
+ include Internal::Timezone
7
+
8
+ class Value < DelegateClass(::Time) # :nodoc:
9
+ end
10
+
11
+ def serialize(value)
12
+ case value = super
13
+ when ::Time
14
+ Value.new(value)
15
+ else
16
+ value
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end