universalid 0.1.2 → 0.1.4

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +387 -1087
  3. data/Rakefile +16 -2
  4. data/config/default.yml +1 -1
  5. data/config/example.yml +2 -2
  6. data/lib/universalid/encoder.rb +21 -0
  7. data/lib/universalid/extensions/active_record/base_message_pack_type.rb +15 -0
  8. data/lib/universalid/extensions/active_record/base_packer.rb +170 -0
  9. data/lib/universalid/extensions/active_record/base_unpacker.rb +61 -0
  10. data/lib/universalid/extensions/active_record/relation_message_pack_type.rb +20 -0
  11. data/lib/universalid/extensions/active_support/time_with_zone_message_pack_type.rb +18 -0
  12. data/lib/universalid/extensions/global_id/global_id_model.rb +28 -0
  13. data/lib/universalid/extensions/global_id/global_id_uid_extension.rb +43 -0
  14. data/lib/universalid/extensions/global_id/message_pack_type.rb +16 -0
  15. data/lib/universalid/extensions/signed_global_id/message_pack_type.rb +12 -0
  16. data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/open_struct.rb +2 -0
  17. data/lib/universalid/message_pack_types/ruby/scalars/bigdecimal.rb +10 -0
  18. data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/date.rb +2 -0
  19. data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/date_time.rb +2 -0
  20. data/lib/{universal_id → universalid}/message_pack_types.rb +3 -5
  21. data/lib/universalid/packer.rb +19 -0
  22. data/lib/{universal_id → universalid}/prepack_database_options.rb +4 -4
  23. data/lib/{universal_id → universalid}/prepack_options.rb +2 -12
  24. data/lib/{universal_id → universalid}/prepacker.rb +3 -3
  25. data/lib/{universal_id → universalid}/refinements/open_struct_refinement.rb +2 -0
  26. data/lib/universalid/refinements.rb +3 -0
  27. data/lib/{universal_id → universalid}/settings.rb +3 -2
  28. data/lib/{universal_id → universalid}/version.rb +1 -1
  29. data/lib/universalid.rb +28 -1
  30. data/lib/uri/uid.rb +17 -7
  31. metadata +128 -50
  32. data/lib/universal_id/contrib/active_record/base_message_pack_type.rb +0 -11
  33. data/lib/universal_id/contrib/active_record/base_packer.rb +0 -130
  34. data/lib/universal_id/contrib/active_record/base_unpacker.rb +0 -50
  35. data/lib/universal_id/contrib/active_record/relation_message_pack_type.rb +0 -16
  36. data/lib/universal_id/contrib/active_record.rb +0 -4
  37. data/lib/universal_id/contrib/active_support/time_with_zone_message_pack_type.rb +0 -14
  38. data/lib/universal_id/contrib/active_support.rb +0 -3
  39. data/lib/universal_id/contrib/global_id/global_id_model.rb +0 -24
  40. data/lib/universal_id/contrib/global_id/global_id_uid_extension.rb +0 -36
  41. data/lib/universal_id/contrib/global_id/message_pack_type.rb +0 -15
  42. data/lib/universal_id/contrib/global_id.rb +0 -3
  43. data/lib/universal_id/contrib/rails.rb +0 -6
  44. data/lib/universal_id/contrib/signed_global_id/message_pack_type.rb +0 -8
  45. data/lib/universal_id/contrib/signed_global_id.rb +0 -3
  46. data/lib/universal_id/encoder.rb +0 -27
  47. data/lib/universal_id/refinements.rb +0 -8
  48. data/lib/universal_id.rb +0 -29
  49. /data/lib/{universal_id → universalid}/message_pack_factory.rb +0 -0
  50. /data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/module.rb +0 -0
  51. /data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/set.rb +0 -0
  52. /data/lib/{universal_id → universalid}/message_pack_types/ruby/composites/struct.rb +0 -0
  53. /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/complex.rb +0 -0
  54. /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/range.rb +0 -0
  55. /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/rational.rb +0 -0
  56. /data/lib/{universal_id → universalid}/message_pack_types/ruby/scalars/regexp.rb +0 -0
  57. /data/lib/{universal_id → universalid}/message_pack_types/uri/uid/type.rb +0 -0
  58. /data/lib/{universal_id → universalid}/refinements/array_refinement.rb +0 -0
  59. /data/lib/{universal_id → universalid}/refinements/hash_refinement.rb +0 -0
  60. /data/lib/{universal_id → universalid}/refinements/set_refinement.rb +0 -0
data/Rakefile CHANGED
@@ -2,9 +2,23 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "minitest/test_task"
5
+ require "pry-byebug"
5
6
 
6
7
  task default: :test
7
8
 
8
- Minitest::TestTask.create(:test) do |t|
9
- t.test_globs = ENV["GLOBS"] ? ENV["GLOBS"].split(",") : ["test/**/*_test.rb"]
9
+ # take explicit control of test initialization
10
+
11
+ task test: [:load_tests, :exec_tests]
12
+
13
+ task :load_tests do
14
+ ENV["TEST_SEED"] ||= ENV.fetch("TEST_SEED", Time.now).to_s
15
+ require_relative "test/test_extension"
16
+
17
+ globs = ENV["GLOBS"] ? ENV["GLOBS"].split(",") : ["test/**/*_test.rb"]
18
+ files = globs.map { |glob| Dir[glob] }.flatten.shuffle
19
+ files.each { |file| require_relative file }
20
+ end
21
+
22
+ Minitest::TestTask.create(:exec_tests) do |t|
23
+ t.test_globs.clear
10
24
  end
data/config/default.yml CHANGED
@@ -7,6 +7,6 @@ prepack:
7
7
  database:
8
8
  include_keys: true
9
9
  include_timestamps: true
10
- include_unsaved_changes: false
10
+ include_changes: false
11
11
  include_descendants: false
12
12
  descendant_depth: 0
data/config/example.yml CHANGED
@@ -13,7 +13,7 @@ prepack:
13
13
  include: []
14
14
 
15
15
  # ..........................................................................................................
16
- # Whether or not to omit blank values when packing (nil, {}, [], "", etc.)
16
+ # Whether or not to include blank values when packing (nil, {}, [], "", etc.)
17
17
  include_blank: true
18
18
 
19
19
  # ==========================================================================================================
@@ -32,7 +32,7 @@ prepack:
32
32
  # ......................................................................................................
33
33
  # Whether or not to include unsaved changes
34
34
  # Assign to `true` when packing new records
35
- include_unsaved_changes: false
35
+ include_changes: false
36
36
 
37
37
  # ......................................................................................................
38
38
  # Whether or not to include loaded in-memory descendants (i.e. child associations)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "brotli"
5
+
6
+ # This module provides the ability to encode and decode objects into a compressed, URL-safe string
7
+ module UniversalID::Encoder
8
+ class << self
9
+ def encode(object, options = {})
10
+ packed = UniversalID::Packer.pack(object, options)
11
+ deflated = Brotli.deflate(packed)
12
+ Base64.urlsafe_encode64 deflated, padding: false
13
+ end
14
+
15
+ def decode(string)
16
+ decoded = Base64.urlsafe_decode64(string)
17
+ inflated = Brotli.inflate(decoded)
18
+ UniversalID::Packer.unpack inflated
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? ActiveRecord::Base
4
+
5
+ require_relative "base_packer"
6
+ require_relative "base_unpacker"
7
+
8
+ UniversalID::MessagePackFactory.register(
9
+ type: ActiveRecord::Base,
10
+ recreate_pool: false,
11
+ packer: ->(obj, packer) { UniversalID::Extensions::ActiveRecordBasePacker.new(obj).pack_with packer },
12
+ unpacker: ->(unpacker) { UniversalID::Extensions::ActiveRecordBaseUnpacker.unpack_with unpacker }
13
+ )
14
+
15
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? ActiveRecord
4
+
5
+ class UniversalID::Extensions::ActiveRecordBasePacker
6
+ using UniversalID::Refinements::HashRefinement
7
+
8
+ # TODO: implement support for has_one
9
+ # ActiveRecord::Reflection::HasOneReflection
10
+ #
11
+ # TODO: implement support for has_and_belongs_to_many
12
+ # ActiveRecord::Reflection::HasAndBelongsToManyReflection
13
+ #
14
+ HAS_MANY_ASSOCIATIONS = [
15
+ ActiveRecord::Reflection::HasManyReflection
16
+ ]
17
+
18
+ DESCENDANTS_KEY = "uid:descendants"
19
+
20
+ attr_reader :record
21
+
22
+ def initialize(record)
23
+ @record = record
24
+ end
25
+
26
+ def pack_with(packer)
27
+ packer.write record.class.name
28
+ packer.write packable_attributes
29
+ end
30
+
31
+ def prepack_options
32
+ options = record.instance_variable_get(:@_uid_prepack_options)
33
+ options = UniversalID::PrepackOptions.new unless options.is_a?(UniversalID::PrepackOptions)
34
+ options
35
+ end
36
+
37
+ def prepack_database_options
38
+ prepack_options.database_options
39
+ end
40
+
41
+ private
42
+
43
+ def packable_attributes
44
+ hash = if id_only?
45
+ record.attributes.slice record.class.primary_key
46
+ else
47
+ record.attributes.select { |name, _| prepack_options.keep_key? name }.tap do |attrs|
48
+ reject_keys! attrs if prepack_database_options.exclude_keys?
49
+ reject_timestamps! attrs if prepack_database_options.exclude_timestamps?
50
+ reject_unsaved_changes! attrs if prepack_database_options.exclude_changes?
51
+ end
52
+ end
53
+
54
+ if include_descendants?
55
+ add_descendants! hash
56
+ hash.delete DESCENDANTS_KEY if hash[DESCENDANTS_KEY].empty?
57
+ end
58
+
59
+ hash["marked_for_destruction"] = true if record.marked_for_destruction?
60
+
61
+ hash.prepack prepack_options
62
+ end
63
+
64
+ # helpers ..................................................................................................
65
+ def id_only?
66
+ return false if record.new_record?
67
+
68
+ # explicit exclusion of primary key
69
+ return false if prepack_options.reject_key?(record.class.primary_key)
70
+
71
+ # explicit exclusion of all db keys and primary key is not explicitly included
72
+ return false if prepack_database_options.exclude_keys? && !prepack_options.keep_key?(record.class.primary_key)
73
+
74
+ # non-pk attribute names
75
+ attribute_names = record.attributes.keys - [record.class.primary_key]
76
+
77
+ # explicit inclusion of non-pk attributes
78
+ return false if prepack_options.includes.any? && attribute_names.any? { |attr| prepack_options.includes[attr] }
79
+
80
+ # record has unsaved non-pk changes and we want to keep them
81
+ return false if prepack_database_options.include_changes? && attribute_names.any? { |attr| record.changes[attr] }
82
+
83
+ prepack_database_options.include_keys?
84
+ end
85
+
86
+ def include_descendants?
87
+ return false unless prepack_database_options.include_descendants?
88
+
89
+ max_depth = prepack_database_options.descendant_depth.to_i
90
+ record_depth = record.instance_variable_get(:@_uid_depth).to_i
91
+ record_depth < max_depth
92
+ end
93
+
94
+ # attribute mutators .......................................................................................
95
+
96
+ def reject_keys!(hash)
97
+ hash.delete record.class.primary_key unless prepack_options.includes[record.class.primary_key]
98
+ foreign_key_column_names.each { |key| hash.delete(key) unless prepack_options.includes[key] }
99
+ end
100
+
101
+ def reject_timestamps!(hash)
102
+ timestamp_column_names.each { |key| hash.delete key unless prepack_options.includes[key] }
103
+ end
104
+
105
+ def reject_unsaved_changes!(hash)
106
+ record.changes_to_save.each do |key, (original_value, _)|
107
+ hash[key] = original_value if prepack_options.keep_key?(key)
108
+ end
109
+ end
110
+
111
+ def add_descendants!(hash)
112
+ hash[DESCENDANTS_KEY] ||= {}
113
+
114
+ has_many_descendant_instances_by_association_name.each do |name, relation|
115
+ descendants = relation.each_with_object([]) do |descendant, memo|
116
+ next unless descendant.persisted? || prepack_database_options.include_changes?
117
+
118
+ descendant.instance_variable_set(:@_uid_depth, prepack_database_options.current_depth + 1)
119
+ prepacked = UniversalID::Prepacker.prepack(descendant, prepack_options.to_h)
120
+ memo << UniversalID::MessagePackFactory.msgpack_pool.dump(prepacked)
121
+ ensure
122
+ prepack_database_options.decrement_current_depth!
123
+ descendant.remove_instance_variable :@_uid_depth
124
+ end
125
+ hash[DESCENDANTS_KEY][name.to_s] = descendants
126
+ end
127
+
128
+ prepack_database_options.increment_current_depth!
129
+ end
130
+
131
+ # active record helpers ....................................................................................
132
+
133
+ def timestamp_column_names
134
+ record.class.all_timestamp_attributes_in_model
135
+ end
136
+
137
+ def foreign_key_column_names
138
+ record.class.reflections
139
+ .each_with_object([]) do |(name, reflection), memo|
140
+ memo << reflection.foreign_key if reflection.macro == :belongs_to
141
+ end
142
+ end
143
+
144
+ def associations
145
+ record.class.reflect_on_all_associations
146
+ end
147
+
148
+ def has_many_associations
149
+ associations.select { |a| HAS_MANY_ASSOCIATIONS.include? a.class }
150
+ end
151
+
152
+ # Returns a has of the current in-memory `has_many` associated records keyed by name
153
+ def has_many_descendant_instances_by_association_name
154
+ has_many_associations.each_with_object({}) do |association, memo|
155
+ relation = record.public_send(association.name)
156
+
157
+ descendants = Set.new
158
+
159
+ # persisted records
160
+ relation.each { |descendant| descendants << descendant } if relation.loaded?
161
+
162
+ # new records
163
+ relation.target.each { |descendant| descendants << descendant } if relation.target.any?
164
+
165
+ memo[association.name] = descendants.to_a if descendants.any?
166
+ end
167
+ end
168
+ end
169
+
170
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? ActiveRecord
4
+
5
+ class UniversalID::Extensions::ActiveRecordBaseUnpacker
6
+ class << self
7
+ def unpack_with(unpacker)
8
+ class_name = unpacker.read
9
+ attributes = unpacker.read || {}
10
+ create_instance class_name, attributes
11
+ end
12
+
13
+ private
14
+
15
+ def create_instance(class_name, attributes)
16
+ klass = Object.const_get(class_name) if Object.const_defined?(class_name)
17
+ return nil unless klass
18
+
19
+ record = if attributes[klass.primary_key]
20
+ klass.find_by(klass.primary_key => attributes[klass.primary_key])
21
+ end
22
+ record ||= klass.new
23
+
24
+ assign_attributes record, attributes.except("marked_for_destruction")
25
+ record.mark_for_destruction if attributes["marked_for_destruction"]
26
+ assign_descendants record, attributes
27
+
28
+ record
29
+ end
30
+
31
+ def assign_attributes(record, attributes)
32
+ attributes.each do |key, value|
33
+ record.public_send :"#{key}=", value if record.respond_to? :"#{key}="
34
+ end
35
+ end
36
+
37
+ def assign_descendants(record, attributes)
38
+ descendants = attributes[UniversalID::Extensions::ActiveRecordBasePacker::DESCENDANTS_KEY] || {}
39
+ descendants.each do |name, list|
40
+ next unless record.respond_to?(name) && record.respond_to?(:"#{name}=")
41
+
42
+ models = list.map { |packed| UniversalID::MessagePackFactory.msgpack_pool.load(packed) }
43
+ models.compact!
44
+ next unless models.any?
45
+
46
+ new_models = models.select(&:new_record?)
47
+ models -= new_models
48
+
49
+ # restore persisted models
50
+ # NOTE: ActiveRecord is smart enough to not re-create or re-add
51
+ # existing records for has_many associations
52
+ record.public_send :"#{name}=", models if models.any?
53
+
54
+ # restore new unsaved models
55
+ record.public_send(name).target.concat new_models if new_models.any?
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? ActiveRecord::Relation
4
+
5
+ # TODO: Revisit ActiveRecord::Relation serialization strategy,
6
+ # and attempt to optimize without falling back to Marshal.dump/load
7
+ UniversalID::MessagePackFactory.register(
8
+ type: ActiveRecord::Relation,
9
+ recreate_pool: false,
10
+ packer: ->(obj, packer) do
11
+ # NOTE: packing a relation will reset the loaded state and internal cache of the relation
12
+ # this ensures minimal payload size
13
+ obj = obj.dup # clear internal cached state (loaded results, etc.)
14
+ obj.reset # dup should clear any internal caching, but we call reset just in case
15
+ packer.write Marshal.dump(obj)
16
+ end,
17
+ unpacker: ->(unpacker) { Marshal.load unpacker.read }
18
+ )
19
+
20
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? ActiveSupport::TimeWithZone
4
+
5
+ UniversalID::MessagePackFactory.register(
6
+ type: ActiveSupport::TimeWithZone,
7
+ packer: ->(obj, packer) do
8
+ packer.write obj.iso8601(9)
9
+ packer.write obj.zone
10
+ end,
11
+ unpacker: ->(unpacker) do
12
+ time = Time.parse(unpacker.read)
13
+ zone = unpacker.read
14
+ ActiveSupport::TimeWithZone.new time, ActiveSupport::TimeZone[zone]
15
+ end
16
+ )
17
+
18
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? GlobalID::Identification
4
+
5
+ class UniversalID::Extensions::GlobalIDModel
6
+ include GlobalID::Identification
7
+
8
+ def self.find(value)
9
+ new value
10
+ end
11
+
12
+ attr_reader :id, :uid
13
+
14
+ def initialize(universal_id)
15
+ @uid = case universal_id
16
+ when URI::UID then universal_id
17
+ when String
18
+ case universal_id
19
+ when /\A#{URI::UID::SCHEME}/o then URI::UID.parse(universal_id)
20
+ else URI::UID.parse(URI::UID.build_string(universal_id, self))
21
+ end
22
+ end
23
+
24
+ @id = @uid&.payload
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? GlobalID::Identification && defined? SignedGlobalID
4
+
5
+ require "forwardable"
6
+ require_relative "global_id_model"
7
+
8
+ module UniversalID::Extensions::GlobalIDUIDExtension
9
+ extend Forwardable
10
+
11
+ def self.included(mixer)
12
+ mixer.extend ClassMethods
13
+ end
14
+
15
+ # Adds all GlobalID::Identification methods
16
+ def_delegators(:to_global_id_model, *GlobalID::Identification.instance_methods(false))
17
+
18
+ # Returns a UniversalID::Extensions::GlobalIDModel instance
19
+ # which implements the GlobalID::Identification interface/protocol
20
+ def to_global_id_model
21
+ UniversalID::Extensions::GlobalIDModel.new self
22
+ end
23
+
24
+ module ClassMethods
25
+ def from_global_id_record(gid_record)
26
+ gid_record&.find&.uid
27
+ end
28
+
29
+ def from_global_id(gid, options = {})
30
+ from_global_id_record GlobalID.parse(gid, options)
31
+ end
32
+
33
+ alias_method :from_gid, :from_global_id
34
+
35
+ def from_signed_global_id(sgid, options = {})
36
+ from_global_id_record SignedGlobalID.parse(sgid, options)
37
+ end
38
+
39
+ alias_method :from_sgid, :from_signed_global_id
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? GlobalID
4
+
5
+ require_relative "global_id_uid_extension"
6
+
7
+ URI::UID.include UniversalID::Extensions::GlobalIDUIDExtension
8
+
9
+ UniversalID::MessagePackFactory.register(
10
+ type: GlobalID,
11
+ recreate_pool: false,
12
+ packer: ->(obj, packer) { packer.write obj.to_param },
13
+ unpacker: ->(unpacker) { GlobalID.parse unpacker.read }
14
+ )
15
+
16
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined? SignedGlobalID
4
+
5
+ UniversalID::MessagePackFactory.register(
6
+ type: SignedGlobalID,
7
+ recreate_pool: false,
8
+ packer: ->(obj, packer) { packer.write obj.to_param },
9
+ unpacker: ->(unpacker) { SignedGlobalID.parse unpacker.read }
10
+ )
11
+
12
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ostruct"
4
+
3
5
  UniversalID::MessagePackFactory.register(
4
6
  type: OpenStruct,
5
7
  recreate_pool: false,
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+
5
+ UniversalID::MessagePackFactory.register_scalar(
6
+ type: BigDecimal,
7
+ recreate_pool: false,
8
+ packer: ->(obj, packer) { packer.write obj.to_s },
9
+ unpacker: ->(unpacker) { BigDecimal unpacker.read }
10
+ )
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "date"
4
+
3
5
  UniversalID::MessagePackFactory.register_scalar(
4
6
  type: Date,
5
7
  recreate_pool: false,
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "date"
4
+
3
5
  UniversalID::MessagePackFactory.register_scalar(
4
6
  type: DateTime,
5
7
  recreate_pool: false,
@@ -3,6 +3,7 @@
3
3
  # NOTE: MessagePack scans registered type in linear order and first match wins
4
4
 
5
5
  # scalars
6
+ require_relative "message_pack_types/ruby/scalars/bigdecimal"
6
7
  require_relative "message_pack_types/ruby/scalars/complex"
7
8
  require_relative "message_pack_types/ruby/scalars/rational"
8
9
  require_relative "message_pack_types/ruby/scalars/date_time"
@@ -16,10 +17,7 @@ require_relative "message_pack_types/ruby/composites/open_struct"
16
17
  require_relative "message_pack_types/ruby/composites/struct"
17
18
  require_relative "message_pack_types/ruby/composites/set"
18
19
 
19
- # uid
20
- require_relative "message_pack_types/uri/uid/type"
21
-
22
- # contribs
23
- require_relative "contrib/rails" if defined? Rails
20
+ # extensions
21
+ Dir["#{__dir__}/extensions/**/*.rb"].each { |f| require f }
24
22
 
25
23
  UniversalID::MessagePackFactory.create_msgpack_pool
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UniversalID::Packer
4
+ class << self
5
+ def pack(object, options = {})
6
+ object = UniversalID::Prepacker.prepack(object, options)
7
+
8
+ # This is basically the same call as UniversalID::MessagePackFactory.pack(object),
9
+ # but it uses a pool of pre-initialized packers/unpackers instead of creating a new one each time
10
+ UniversalID::MessagePackFactory.msgpack_pool.dump object
11
+ end
12
+
13
+ def unpack(string)
14
+ # This is basically the same call as UniversalID::MessagePackFactory.unpack(object),
15
+ # but it uses a pool of pre-initialized packers/unpackers instead of creating a new one each time
16
+ UniversalID::MessagePackFactory.msgpack_pool.load string
17
+ end
18
+ end
19
+ end
@@ -25,12 +25,12 @@ class UniversalID::PrepackDatabaseOptions
25
25
  !include_timestamps?
26
26
  end
27
27
 
28
- def include_unsaved_changes?
29
- !!@settings.include_unsaved_changes
28
+ def include_changes?
29
+ !!@settings.include_changes
30
30
  end
31
31
 
32
- def exclude_unsaved_changes?
33
- !include_unsaved_changes?
32
+ def exclude_changes?
33
+ !include_changes?
34
34
  end
35
35
 
36
36
  def descendant_depth
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "prepack_database_options"
4
-
5
3
  class UniversalID::PrepackOptions
6
4
  attr_reader :excludes, :includes, :database_options
7
5
 
@@ -37,7 +35,7 @@ class UniversalID::PrepackOptions
37
35
  end
38
36
 
39
37
  def reject_key?(key)
40
- excludes[key.to_s]
38
+ !!excludes[key.to_s]
41
39
  end
42
40
 
43
41
  def keep_value?(value)
@@ -57,15 +55,7 @@ class UniversalID::PrepackOptions
57
55
  end
58
56
 
59
57
  def blank?(value)
60
- return true if value.nil?
61
- return false if !!value == value # booleans
62
-
63
- result = false
64
- result ||= value.empty? if value.respond_to?(:empty?)
65
- result ||= value.blank? if value.respond_to?(:blank?)
66
- result ||= value.strip.empty? if value.is_a?(String)
67
- result ||= value.compact.empty? if value.is_a?(Array) || value.is_a?(Hash)
68
- result
58
+ (value == false) ? false : value.blank?
69
59
  end
70
60
 
71
61
  def present?(value)
@@ -15,11 +15,11 @@ class UniversalID::Prepacker
15
15
  class << self
16
16
  def prepack(object, options = {})
17
17
  options = UniversalID::PrepackOptions.new(options) unless options.is_a?(UniversalID::PrepackOptions)
18
+ object.instance_variable_set(:@_uid_prepack_options, options) unless object.frozen?
18
19
 
19
- return object.prepack(options) if object.respond_to?(:prepack)
20
+ return object unless object.respond_to?(:prepack)
20
21
 
21
- object.instance_variable_set(:@_uid_prepack_options, options) unless object.frozen?
22
- object
22
+ object.prepack options
23
23
  end
24
24
  end
25
25
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "ostruct"
4
+
3
5
  module UniversalID::Refinements::OpenStructRefinement
4
6
  refine OpenStruct do
5
7
  using UniversalID::Refinements::HashRefinement
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UniversalID::Refinements; end
@@ -38,7 +38,8 @@ class UniversalID::Settings
38
38
  in database: database then database.each { |k, v| assign k, v, to: to }
39
39
  in include_keys: include_keys then to.prepack.database.include_keys = !!include_keys
40
40
  in include_timestamps: include_timestamps then to.prepack.database.include_timestamps = !!include_timestamps
41
- in include_unsaved_changes: include_unsaved_changes then to.prepack.database.include_unsaved_changes = !!include_unsaved_changes
41
+ in include_changes: include_changes then to.prepack.database.include_changes = !!include_changes
42
+ in include_unsaved_changes: include_changes then to.prepack.database.include_changes = !!include_unsaved_changes # TODO: Remove in v1.0
42
43
  in include_descendants: include_descendants then to.prepack.database.include_descendants = !!include_descendants
43
44
  in descendant_depth: descendant_depth then to.prepack.database.descendant_depth = descendant_depth
44
45
  else # ignore key
@@ -60,7 +61,7 @@ class UniversalID::Settings
60
61
  config = self.class.build(**config) unless key == :default
61
62
  registry[key] = config
62
63
  self.class.define_method(key) { config }
63
- self.class.define_method("#{key}_copy") { Marshal.load Marshal.dump(config) }
64
+ self.class.define_method(:"#{key}_copy") { Marshal.load Marshal.dump(config) }
64
65
  self.class.define_singleton_method(key) { instance.public_send key }
65
66
  [key, config]
66
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UniversalID
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.4"
5
5
  end