universalid 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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