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,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module AttributeDecorators # :nodoc:
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ class_attribute :attribute_type_decorations, instance_accessor: false, default: TypeDecorator.new # :internal:
9
+ end
10
+
11
+ module ClassMethods # :nodoc:
12
+ # This method is an internal API used to create class macros such as
13
+ # +serialize+, and features like time zone aware attributes.
14
+ #
15
+ # Used to wrap the type of an attribute in a new type.
16
+ # When the schema for a model is loaded, attributes with the same name as
17
+ # +column_name+ will have their type yielded to the given block. The
18
+ # return value of that block will be used instead.
19
+ #
20
+ # Subsequent calls where +column_name+ and +decorator_name+ are the same
21
+ # will override the previous decorator, not decorate twice. This can be
22
+ # used to create idempotent class macros like +serialize+
23
+ def decorate_attribute_type(column_name, decorator_name, &block)
24
+ matcher = ->(name, _) { name == column_name.to_s }
25
+ key = "_#{column_name}_#{decorator_name}"
26
+ decorate_matching_attribute_types(matcher, key, &block)
27
+ end
28
+
29
+ # This method is an internal API used to create higher level features like
30
+ # time zone aware attributes.
31
+ #
32
+ # When the schema for a model is loaded, +matcher+ will be called for each
33
+ # attribute with its name and type. If the matcher returns a truthy value,
34
+ # the type will then be yielded to the given block, and the return value
35
+ # of that block will replace the type.
36
+ #
37
+ # Subsequent calls to this method with the same value for +decorator_name+
38
+ # will replace the previous decorator, not decorate twice. This can be
39
+ # used to ensure that class macros are idempotent.
40
+ def decorate_matching_attribute_types(matcher, decorator_name, &block)
41
+ reload_schema_from_cache
42
+ decorator_name = decorator_name.to_s
43
+
44
+ # Create new hashes so we don't modify parent classes
45
+ self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
46
+ end
47
+
48
+ private
49
+
50
+ def load_schema!
51
+ super
52
+ attribute_types.each do |name, type|
53
+ decorated_type = attribute_type_decorations.apply(name, type)
54
+ define_attribute(name, decorated_type)
55
+ end
56
+ end
57
+ end
58
+
59
+ class TypeDecorator # :nodoc:
60
+ delegate :clear, to: :@decorations
61
+
62
+ def initialize(decorations = {})
63
+ @decorations = decorations
64
+ end
65
+
66
+ def merge(*args)
67
+ TypeDecorator.new(@decorations.merge(*args))
68
+ end
69
+
70
+ def apply(name, type)
71
+ decorations = decorators_for(name, type)
72
+ decorations.inject(type) do |new_type, block|
73
+ block.call(new_type)
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def decorators_for(name, type)
80
+ matching(name, type).map(&:last)
81
+ end
82
+
83
+ def matching(name, type)
84
+ @decorations.values.select do |(matcher, _)|
85
+ matcher.call(name, type)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module AttributeMethods
5
+ # = Active Entity Attribute Methods Before Type Cast
6
+ #
7
+ # ActiveEntity::AttributeMethods::BeforeTypeCast provides a way to
8
+ # read the value of the attributes before typecasting and deserialization.
9
+ #
10
+ # class Task < ActiveEntity::Base
11
+ # end
12
+ #
13
+ # task = Task.new(id: '1', completed_on: '2012-10-21')
14
+ # task.id # => 1
15
+ # task.completed_on # => Sun, 21 Oct 2012
16
+ #
17
+ # task.attributes_before_type_cast
18
+ # # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
19
+ # task.read_attribute_before_type_cast('id') # => "1"
20
+ # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
21
+ #
22
+ # In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
23
+ # it declares a method for all attributes with the <tt>*_before_type_cast</tt>
24
+ # suffix.
25
+ #
26
+ # task.id_before_type_cast # => "1"
27
+ # task.completed_on_before_type_cast # => "2012-10-21"
28
+ module BeforeTypeCast
29
+ extend ActiveSupport::Concern
30
+
31
+ included do
32
+ attribute_method_suffix "_before_type_cast"
33
+ attribute_method_suffix "_came_from_user?"
34
+ end
35
+
36
+ # Returns the value of the attribute identified by +attr_name+ before
37
+ # typecasting and deserialization.
38
+ #
39
+ # class Task < ActiveEntity::Base
40
+ # end
41
+ #
42
+ # task = Task.new(id: '1', completed_on: '2012-10-21')
43
+ # task.read_attribute('id') # => 1
44
+ # task.read_attribute_before_type_cast('id') # => '1'
45
+ # task.read_attribute('completed_on') # => Sun, 21 Oct 2012
46
+ # task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
47
+ # task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
48
+ def read_attribute_before_type_cast(attr_name)
49
+ attribute_before_type_cast(attr_name.to_s)
50
+ end
51
+
52
+ # Returns a hash of attributes before typecasting and deserialization.
53
+ #
54
+ # class Task < ActiveEntity::Base
55
+ # end
56
+ #
57
+ # task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
58
+ # task.attributes
59
+ # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
60
+ # task.attributes_before_type_cast
61
+ # # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
62
+ def attributes_before_type_cast
63
+ @attributes.values_before_type_cast
64
+ end
65
+
66
+ private
67
+
68
+ # Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
69
+ def attribute_before_type_cast(attr_name)
70
+ @attributes[attr_name].value_before_type_cast
71
+ end
72
+
73
+ def attribute_came_from_user?(attr_name)
74
+ @attributes[attr_name].came_from_user?
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+
5
+ module ActiveEntity
6
+ module AttributeMethods
7
+ module Dirty
8
+ extend ActiveSupport::Concern
9
+ include ActiveEntity::AMAttributeMethods
10
+
11
+ included do
12
+ attribute_method_suffix "_changed?", "_change", "_will_change!", "_was"
13
+ attribute_method_suffix "_previously_changed?", "_previous_change", "_previously_was"
14
+ attribute_method_affix prefix: "restore_", suffix: "!"
15
+ attribute_method_affix prefix: "clear_", suffix: "_change"
16
+ end
17
+
18
+ def initialize_dup(other) # :nodoc:
19
+ super
20
+ if self.class.respond_to?(:_default_attributes)
21
+ @attributes = self.class._default_attributes.map do |attr|
22
+ attr.with_value_from_user(@attributes.fetch_value(attr.name))
23
+ end
24
+ end
25
+ @mutations_from_database = nil
26
+ end
27
+
28
+ def as_json(options = {}) # :nodoc:
29
+ options[:except] = [*options[:except], "mutations_from_database", "mutations_before_last_save"]
30
+ super(options)
31
+ end
32
+
33
+ # Clears dirty data and moves +changes+ to +previous_changes+ and
34
+ # +mutations_from_database+ to +mutations_before_last_save+ respectively.
35
+ def changes_applied
36
+ unless defined?(@attributes)
37
+ mutations_from_database.finalize_changes
38
+ end
39
+ @mutations_before_last_save = mutations_from_database
40
+ forget_attribute_assignments
41
+ @mutations_from_database = nil
42
+ end
43
+
44
+ # Returns +true+ if any of the attributes has unsaved changes, +false+ otherwise.
45
+ #
46
+ # person.changed? # => false
47
+ # person.name = 'bob'
48
+ # person.changed? # => true
49
+ def changed?
50
+ mutations_from_database.any_changes?
51
+ end
52
+
53
+ # Returns an array with the name of the attributes with unsaved changes.
54
+ #
55
+ # person.changed # => []
56
+ # person.name = 'bob'
57
+ # person.changed # => ["name"]
58
+ def changed
59
+ mutations_from_database.changed_attribute_names
60
+ end
61
+
62
+ # Dispatch target for <tt>*_changed?</tt> attribute methods.
63
+ def attribute_changed?(attr_name, **options) # :nodoc:
64
+ mutations_from_database.changed?(attr_name.to_s, **options)
65
+ end
66
+
67
+ # Dispatch target for <tt>*_was</tt> attribute methods.
68
+ def attribute_was(attr_name) # :nodoc:
69
+ mutations_from_database.original_value(attr_name.to_s)
70
+ end
71
+
72
+ # Dispatch target for <tt>*_previously_changed?</tt> attribute methods.
73
+ def attribute_previously_changed?(attr_name, **options) # :nodoc:
74
+ mutations_before_last_save.changed?(attr_name.to_s, **options)
75
+ end
76
+
77
+ # Dispatch target for <tt>*_previously_was</tt> attribute methods.
78
+ def attribute_previously_was(attr_name) # :nodoc:
79
+ mutations_before_last_save.original_value(attr_name.to_s)
80
+ end
81
+
82
+ # Restore all previous data of the provided attributes.
83
+ def restore_attributes(attr_names = changed)
84
+ attr_names.each { |attr_name| restore_attribute!(attr_name) }
85
+ end
86
+
87
+ # Clears all dirty data: current changes and previous changes.
88
+ def clear_changes_information
89
+ @mutations_before_last_save = nil
90
+ forget_attribute_assignments
91
+ @mutations_from_database = nil
92
+ end
93
+
94
+ def clear_attribute_changes(attr_names)
95
+ attr_names.each do |attr_name|
96
+ clear_attribute_change(attr_name)
97
+ end
98
+ end
99
+
100
+ # Returns a hash of the attributes with unsaved changes indicating their original
101
+ # values like <tt>attr => original value</tt>.
102
+ #
103
+ # person.name # => "bob"
104
+ # person.name = 'robert'
105
+ # person.changed_attributes # => {"name" => "bob"}
106
+ def changed_attributes
107
+ mutations_from_database.changed_values
108
+ end
109
+
110
+ # Returns a hash of changed attributes indicating their original
111
+ # and new values like <tt>attr => [original value, new value]</tt>.
112
+ #
113
+ # person.changes # => {}
114
+ # person.name = 'bob'
115
+ # person.changes # => { "name" => ["bill", "bob"] }
116
+ def changes
117
+ mutations_from_database.changes
118
+ end
119
+
120
+ # Returns a hash of attributes that were changed before the model was saved.
121
+ #
122
+ # person.name # => "bob"
123
+ # person.name = 'robert'
124
+ # person.save
125
+ # person.previous_changes # => {"name" => ["bob", "robert"]}
126
+ def previous_changes
127
+ mutations_before_last_save.changes
128
+ end
129
+
130
+ def attribute_changed_in_place?(attr_name) # :nodoc:
131
+ mutations_from_database.changed_in_place?(attr_name.to_s)
132
+ end
133
+
134
+ private
135
+ def clear_attribute_change(attr_name)
136
+ mutations_from_database.forget_change(attr_name.to_s)
137
+ end
138
+
139
+ def mutations_from_database
140
+ @mutations_from_database ||= if defined?(@attributes)
141
+ ActiveModel::AttributeMutationTracker.new(@attributes)
142
+ else
143
+ ActiveModel::ForcedMutationTracker.new(self)
144
+ end
145
+ end
146
+
147
+ def forget_attribute_assignments
148
+ @attributes = @attributes.map(&:forgetting_assignment) if defined?(@attributes)
149
+ end
150
+
151
+ def mutations_before_last_save
152
+ @mutations_before_last_save ||= ActiveModel::NullMutationTracker.instance
153
+ end
154
+
155
+ # Dispatch target for <tt>*_change</tt> attribute methods.
156
+ def attribute_change(attr_name)
157
+ mutations_from_database.change_to_attribute(attr_name.to_s)
158
+ end
159
+
160
+ # Dispatch target for <tt>*_previous_change</tt> attribute methods.
161
+ def attribute_previous_change(attr_name)
162
+ mutations_before_last_save.change_to_attribute(attr_name.to_s)
163
+ end
164
+
165
+ # Dispatch target for <tt>*_will_change!</tt> attribute methods.
166
+ def attribute_will_change!(attr_name)
167
+ mutations_from_database.force_change(attr_name.to_s)
168
+ end
169
+
170
+ # Dispatch target for <tt>restore_*!</tt> attribute methods.
171
+ def restore_attribute!(attr_name)
172
+ attr_name = attr_name.to_s
173
+ if attribute_changed?(attr_name)
174
+ __send__("#{attr_name}=", attribute_was(attr_name))
175
+ clear_attribute_change(attr_name)
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module ActiveEntity
6
+ module AttributeMethods
7
+ module PrimaryKey
8
+ extend ActiveSupport::Concern
9
+
10
+ # Returns this record's primary key value wrapped in an array if one is
11
+ # available.
12
+ def to_key
13
+ key = id
14
+ [key] if key
15
+ end
16
+
17
+ # Returns the primary key column's value.
18
+ def id
19
+ _read_attribute(@primary_key)
20
+ end
21
+
22
+ # Sets the primary key column's value.
23
+ def id=(value)
24
+ _write_attribute(@primary_key, value)
25
+ end
26
+
27
+ # Queries the primary key column's value.
28
+ def id?
29
+ query_attribute(@primary_key)
30
+ end
31
+
32
+ # Returns the primary key column's value before type cast.
33
+ def id_before_type_cast
34
+ read_attribute_before_type_cast(@primary_key)
35
+ end
36
+
37
+ # Returns the primary key column's previous value.
38
+ def id_was
39
+ attribute_was(@primary_key)
40
+ end
41
+
42
+ private
43
+
44
+ def attribute_method?(attr_name)
45
+ attr_name == "id" || super
46
+ end
47
+
48
+ module ClassMethods
49
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
50
+
51
+ def instance_method_already_implemented?(method_name)
52
+ super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
53
+ end
54
+
55
+ def dangerous_attribute_method?(method_name)
56
+ super && !ID_ATTRIBUTE_METHODS.include?(method_name)
57
+ end
58
+
59
+ # Defines the primary key field -- can be overridden in subclasses.
60
+ # Overwriting will negate any effect of the +primary_key_prefix_type+
61
+ # setting, though.
62
+ def primary_key
63
+ unless defined? @primary_key
64
+ @primary_key =
65
+ if has_attribute?("id")
66
+ "id"
67
+ else
68
+ nil
69
+ end
70
+ end
71
+
72
+ @primary_key
73
+ end
74
+
75
+ # Sets the name of the primary key column.
76
+ #
77
+ # class Project < ActiveEntity::Base
78
+ # self.primary_key = 'sysid'
79
+ # end
80
+ #
81
+ # You can also define the #primary_key method yourself:
82
+ #
83
+ # class Project < ActiveEntity::Base
84
+ # def self.primary_key
85
+ # 'foo_' + super
86
+ # end
87
+ # end
88
+ #
89
+ # Project.primary_key # => "foo_id"
90
+ def primary_key=(value)
91
+ @primary_key = value && -value.to_s
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module AttributeMethods
5
+ module Query
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attribute_method_suffix "?"
10
+ end
11
+
12
+ def query_attribute(attr_name)
13
+ value = self[attr_name]
14
+
15
+ case value
16
+ when true then true
17
+ when false, nil then false
18
+ else
19
+ if !type_for_attribute(attr_name) { false }
20
+ if Numeric === value || !value.match?(/[^0-9]/)
21
+ !value.to_i.zero?
22
+ else
23
+ return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
24
+ !value.blank?
25
+ end
26
+ elsif value.respond_to?(:zero?)
27
+ !value.zero?
28
+ else
29
+ !value.blank?
30
+ end
31
+ end
32
+ end
33
+
34
+ alias :attribute? :query_attribute
35
+ private :attribute?
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module AttributeMethods
5
+ module Read
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods # :nodoc:
9
+ private
10
+
11
+ def define_method_attribute(name, owner:)
12
+ ActiveEntity::AMAttributeMethods::AttrNames.define_attribute_accessor_method(
13
+ owner, name
14
+ ) do |temp_method_name, attr_name_expr|
15
+ owner <<
16
+ "def #{temp_method_name}" <<
17
+ " _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
18
+ "end"
19
+ end
20
+ end
21
+ end
22
+
23
+ # Returns the value of the attribute identified by <tt>attr_name</tt> after
24
+ # it has been typecast (for example, "2004-12-12" in a date column is cast
25
+ # to a date object, like Date.new(2004, 12, 12)).
26
+ def read_attribute(attr_name, &block)
27
+ name = attr_name.to_s
28
+ name = self.class.attribute_aliases[name] || name
29
+
30
+ name = @primary_key if name == "id" && @primary_key
31
+ @attributes.fetch_value(name, &block)
32
+ end
33
+
34
+ # This method exists to avoid the expensive primary_key check internally, without
35
+ # breaking compatibility with the read_attribute API
36
+ def _read_attribute(attr_name, &block) # :nodoc
37
+ @attributes.fetch_value(attr_name, &block)
38
+ end
39
+
40
+ alias :attribute :_read_attribute
41
+ private :attribute
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveEntity
4
+ module AttributeMethods
5
+ module Serialization
6
+ extend ActiveSupport::Concern
7
+
8
+ class ColumnNotSerializableError < StandardError
9
+ def initialize(name, type)
10
+ super <<~EOS
11
+ Column `#{name}` of type #{type.class} does not support `serialize` feature.
12
+ Usually it means that you are trying to use `serialize`
13
+ on a column that already implements serialization natively.
14
+ EOS
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ # If you have an attribute that needs to be saved to the database as an
20
+ # object, and retrieved as the same object, then specify the name of that
21
+ # attribute using this method and it will be handled automatically. The
22
+ # serialization is done through YAML. If +class_name+ is specified, the
23
+ # serialized object must be of that class on assignment and retrieval.
24
+ # Otherwise SerializationTypeMismatch will be raised.
25
+ #
26
+ # Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of
27
+ # +Array+, will always be persisted as null.
28
+ #
29
+ # Keep in mind that database adapters handle certain serialization tasks
30
+ # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
31
+ # converted between JSON object/array syntax and Ruby +Hash+ or +Array+
32
+ # objects transparently. There is no need to use #serialize in this
33
+ # case.
34
+ #
35
+ # For more complex cases, such as conversion to or from your application
36
+ # domain objects, consider using the ActiveEntity::Attributes API.
37
+ #
38
+ # ==== Parameters
39
+ #
40
+ # * +attr_name+ - The field name that should be serialized.
41
+ # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
42
+ # or a class name that the object type should be equal to.
43
+ #
44
+ # ==== Example
45
+ #
46
+ # # Serialize a preferences attribute.
47
+ # class User < ActiveEntity::Base
48
+ # serialize :preferences
49
+ # end
50
+ #
51
+ # # Serialize preferences using JSON as coder.
52
+ # class User < ActiveEntity::Base
53
+ # serialize :preferences, JSON
54
+ # end
55
+ #
56
+ # # Serialize preferences as Hash using YAML coder.
57
+ # class User < ActiveEntity::Base
58
+ # serialize :preferences, Hash
59
+ # end
60
+ def serialize(attr_name, class_name_or_coder = Object)
61
+ # When ::JSON is used, force it to go through the Active Support JSON encoder
62
+ # to ensure special objects (e.g. Active Entity models) are dumped correctly
63
+ # using the #as_json hook.
64
+ coder = if class_name_or_coder == ::JSON
65
+ Coders::JSON
66
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
67
+ class_name_or_coder
68
+ else
69
+ Coders::YAMLColumn.new(attr_name, class_name_or_coder)
70
+ end
71
+
72
+ decorate_attribute_type(attr_name, :serialize) do |type|
73
+ if type_incompatible_with_serialize?(type, class_name_or_coder)
74
+ raise ColumnNotSerializableError.new(attr_name, type)
75
+ end
76
+
77
+ Type::Serialized.new(type, coder)
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def type_incompatible_with_serialize?(type, class_name)
84
+ type.is_a?(ActiveEntity::Type::Json) && class_name == ::JSON ||
85
+ type.respond_to?(:type_cast_array, true) && class_name == ::Array
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end