universalid 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +49 -8
- data/lib/universalid/extensions/active_record/base_unpacker.rb +5 -1
- data/lib/universalid/extensions/active_support/time_with_zone_message_pack_type.rb +5 -5
- data/lib/universalid/extensions/global_id/global_id_model.rb +4 -6
- data/lib/universalid/extensions/global_id/global_id_uid_extension.rb +1 -1
- data/lib/universalid/message_pack_types/{ruby/composites → composites}/struct.rb +2 -1
- data/lib/universalid/message_pack_types.rb +12 -12
- data/lib/universalid/settings.rb +1 -1
- data/lib/universalid/version.rb +1 -1
- data/lib/uri/uid.rb +24 -4
- metadata +29 -15
- /data/lib/universalid/message_pack_types/{ruby/composites → composites}/module.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/composites → composites}/open_struct.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/composites → composites}/set.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/bigdecimal.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/complex.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/date.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/date_time.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/range.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/rational.rb +0 -0
- /data/lib/universalid/message_pack_types/{ruby/scalars → scalars}/regexp.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 626443be00555a2fac0c698df128de29ad2169003bc293e1e1df63ff83cfaf19
|
4
|
+
data.tar.gz: d4f5f42845c42332abbe2b64e5a2fa4139f08e457f9766cce700735003d88e02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e52fef154f9e9dd5660d413707cb2b05619cde795aaf9034ddc311bebe699a40332be0cdc638cc5305bd357473a61f96c15855f88cad8e0e0cbfe33ce56bfb49
|
7
|
+
data.tar.gz: 65f63606908e4a22117285d463b5d96878108320ee92fba133d22ec0fe4f8673cddab66a6a61e95fc3a3bf1e876364d03ee2de3541473da8b3f169899cd865bd
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<p>
|
4
4
|
<a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
|
5
|
-
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-
|
5
|
+
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-811-47d299.svg" />
|
6
6
|
</a>
|
7
7
|
<a href="https://codeclimate.com/github/hopsoft/universalid/maintainability">
|
8
8
|
<img src="https://api.codeclimate.com/v1/badges/567624cbe733fafc2330/maintainability" />
|
@@ -33,12 +33,12 @@
|
|
33
33
|
</a>
|
34
34
|
</p>
|
35
35
|
|
36
|
-
## Fast, recursive,
|
36
|
+
## Fast, recursive, optimized, URL-Safe serialization for any Ruby object
|
37
37
|
|
38
38
|
Universal ID leverages both [MessagePack](https://msgpack.org/) and [Brotli](https://github.com/google/brotli) _(a combo built for speed and best-in-class data compression)_.
|
39
39
|
When combined, these libraries are up to 30% faster and within 2-5% compression rates compared to Protobuf. <a title="Source" href="https://g.co/bard/share/e5bdb17aee91">↗</a>
|
40
40
|
|
41
|
-
Universal ID
|
41
|
+
Universal ID introduces a paradigm shift that enables straightforward simple [**solutions** ↗](docs/use_cases.md) for a variety of complex problem domains.
|
42
42
|
|
43
43
|
> [!TIP]
|
44
44
|
> All the code examples below can be tested on your local machine. Just clone the repo _(↑or use Gitpod above↑)_ and run `bin/console` to begin exploring.
|
@@ -48,6 +48,7 @@ Universal ID opens the flood gates with a deluge of powerful yet easily implemen
|
|
48
48
|
|
49
49
|
## Table of Contents
|
50
50
|
|
51
|
+
- [URI::UID](#uriuid)
|
51
52
|
- [Supported Data Types](#supported-data-types)
|
52
53
|
- [Primitive Types](#primitive-types)
|
53
54
|
- [Composite Types](#composite-types)
|
@@ -64,6 +65,46 @@ Universal ID opens the flood gates with a deluge of powerful yet easily implemen
|
|
64
65
|
|
65
66
|
<!-- Tocer[finish]: Auto-generated, don't remove. -->
|
66
67
|
|
68
|
+
## URI::UID
|
69
|
+
|
70
|
+
Universal ID introduces a new URI defintion that can recursively serialize any Ruby object into an URL-safe string
|
71
|
+
which can be safely transported via HTTP.
|
72
|
+
|
73
|
+
> [!NOTE]
|
74
|
+
> The payload is optimized to be as small as possible... _especially notable with large objects._
|
75
|
+
|
76
|
+
The best part: **The API is simple.**
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
data = :ANY_OBJECT_YOU_CAN_IMAGINE
|
80
|
+
|
81
|
+
uid = URI::UID.build(data)
|
82
|
+
#<URI::UID payload=Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQ..., fingerprint=CwWAkccHf6ZTeW1ib2wD>
|
83
|
+
|
84
|
+
uid.payload
|
85
|
+
"Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQUdJTkUD"
|
86
|
+
|
87
|
+
uid.fingerprint
|
88
|
+
"CwWAkccHf6ZTeW1ib2wD"
|
89
|
+
|
90
|
+
uri = uid.to_s
|
91
|
+
"uid://universalid/Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQUdJTkUD#CwWAkccHf6ZTeW1ib2wD"
|
92
|
+
|
93
|
+
parsed = URI::UID.parse(uri)
|
94
|
+
#<URI::UID payload=Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQ..., fingerprint=CwWAkccHf6ZTeW1ib2wD>
|
95
|
+
|
96
|
+
parsed.decode
|
97
|
+
:ANY_OBJECT_YOU_CAN_IMAGINE
|
98
|
+
|
99
|
+
# it's also possible to parse the payload by itself
|
100
|
+
|
101
|
+
parsed = URI::UID.from_payload(uid.payload)
|
102
|
+
#<URI::UID payload=Cw6AxxoAQU5ZX09CSkVDVF9ZT1VfQ0FOX0lNQ..., fingerprint=CwWAkccHf6ZTeW1ib2wD>
|
103
|
+
|
104
|
+
parsed.decode
|
105
|
+
:ANY_OBJECT_YOU_CAN_IMAGINE
|
106
|
+
```
|
107
|
+
|
67
108
|
## Supported Data Types
|
68
109
|
|
69
110
|
### Primitive Types
|
@@ -101,7 +142,7 @@ uid.decode
|
|
101
142
|
|
102
143
|
### Composite Types
|
103
144
|
|
104
|
-
Composite _(or
|
145
|
+
Composite _(or complex, compound, etc.)_ datatype support is where things start to get interesting.
|
105
146
|
Universal ID supports the following native Ruby composite datatypes:
|
106
147
|
|
107
148
|
- `Array`
|
@@ -175,7 +216,7 @@ The following extension datatypes ship with Universal ID:
|
|
175
216
|
> [!IMPORTANT]
|
176
217
|
> **Why Universal ID with ActiveRecord?**
|
177
218
|
> ActiveRecord already has GlobalID, a robust library for serializing individual models.
|
178
|
-
> Universal ID covers a much
|
219
|
+
> **Universal ID covers a much wider range of use cases**.
|
179
220
|
|
180
221
|
Here are a few reasons you may want to consider Universal ID with ActiveRecord.
|
181
222
|
|
@@ -195,7 +236,7 @@ Here are a few reasons you may want to consider Universal ID with ActiveRecord.
|
|
195
236
|
Universal ID gives you control over the serialization process. You can choose which columns to include/exclude, allowing for tailored, optimized payloads to fit your needs.
|
196
237
|
|
197
238
|
- **Queries/Relations**:
|
198
|
-
Universal ID
|
239
|
+
Universal ID also supports ActiveRecord::Relations, enabling the serialization of complex database queries and scopes.
|
199
240
|
|
200
241
|
In summary, while GlobalID excels in its specific use case, Universal ID offers more power for use-cases that involve unsaved records, complex associations, data cloning, and database queries.
|
201
242
|
|
@@ -395,7 +436,7 @@ Fingerprints are comprised of the following components:
|
|
395
436
|
1. `Class (Class)` - The encoded object's class
|
396
437
|
2. `Timestamp (Time)` - The `mtime` (UTC) of the file that defined the object's class
|
397
438
|
|
398
|
-
Fingerprints
|
439
|
+
Fingerprints provide a simple mechanism to help manage data format versions... **minimizing the need for custom versioning solutions**.
|
399
440
|
Whenever the class definition changes, the `mtime` updates, resulting in a different fingerprint.
|
400
441
|
This is especially useful in scenarios where the data format evolves over time, such as in long-lived applications.
|
401
442
|
|
@@ -466,7 +507,7 @@ copy.save #=> true
|
|
466
507
|
```
|
467
508
|
|
468
509
|
> [!TIP]
|
469
|
-
> If you don't need a URL-Safe UID, you can use `UniversalID::Packer` to speed things up.
|
510
|
+
> If you don't need a URL-Safe UID, you can use `UniversalID::Packer` to speed things up a bit.
|
470
511
|
|
471
512
|
```ruby
|
472
513
|
packed = UniversalID::Packer.pack(campaign, options)
|
@@ -45,6 +45,7 @@ if defined? ActiveRecord
|
|
45
45
|
|
46
46
|
new_models = models.select(&:new_record?)
|
47
47
|
models -= new_models
|
48
|
+
association_collection = record.public_send(name)
|
48
49
|
|
49
50
|
# restore persisted models
|
50
51
|
# NOTE: ActiveRecord is smart enough to not re-create or re-add
|
@@ -52,7 +53,10 @@ if defined? ActiveRecord
|
|
52
53
|
record.public_send :"#{name}=", models if models.any?
|
53
54
|
|
54
55
|
# restore new unsaved models
|
55
|
-
|
56
|
+
association_collection.target.concat new_models if new_models.any?
|
57
|
+
|
58
|
+
# mark association relation as loaded
|
59
|
+
association_collection.proxy_association.instance_variable_set :@loaded, true
|
56
60
|
end
|
57
61
|
end
|
58
62
|
end
|
@@ -5,13 +5,13 @@ if defined? ActiveSupport::TimeWithZone
|
|
5
5
|
UniversalID::MessagePackFactory.register(
|
6
6
|
type: ActiveSupport::TimeWithZone,
|
7
7
|
packer: ->(obj, packer) do
|
8
|
-
packer.write obj.
|
9
|
-
packer.write obj.
|
8
|
+
packer.write obj.to_time.utc
|
9
|
+
packer.write obj.time_zone.tzinfo.identifier
|
10
10
|
end,
|
11
11
|
unpacker: ->(unpacker) do
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
utc = unpacker.read
|
13
|
+
tz = unpacker.read
|
14
|
+
utc.in_time_zone ActiveSupport::TimeZone[tz]
|
15
15
|
end
|
16
16
|
)
|
17
17
|
|
@@ -14,14 +14,12 @@ if defined? GlobalID::Identification
|
|
14
14
|
def initialize(universal_id)
|
15
15
|
@uid = case universal_id
|
16
16
|
when URI::UID then universal_id
|
17
|
-
when String
|
18
|
-
|
19
|
-
|
20
|
-
else URI::UID.parse(URI::UID.build_string(universal_id, self))
|
21
|
-
end
|
17
|
+
when String then URI::UID.match?(universal_id) ?
|
18
|
+
URI::UID.parse(universal_id) :
|
19
|
+
URI::UID.from_payload(universal_id)
|
22
20
|
end
|
23
21
|
|
24
|
-
@id =
|
22
|
+
@id = uid&.payload
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
@@ -11,7 +11,8 @@ UniversalID::MessagePackFactory.register(
|
|
11
11
|
unpacker: ->(unpacker) do
|
12
12
|
class_name = unpacker.read
|
13
13
|
hash = unpacker.read
|
14
|
-
klass = Object.const_get(class_name) if Object.const_defined?(class_name)
|
14
|
+
klass = Object.const_get(class_name) if class_name && Object.const_defined?(class_name)
|
15
|
+
klass ||= Struct.new(*hash.keys)
|
15
16
|
|
16
17
|
if klass
|
17
18
|
# shenanigans to support ::Ruby 3.0.X and 3.1.X
|
@@ -3,21 +3,21 @@
|
|
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/
|
7
|
-
require_relative "message_pack_types/
|
8
|
-
require_relative "message_pack_types/
|
9
|
-
require_relative "message_pack_types/
|
10
|
-
require_relative "message_pack_types/
|
11
|
-
require_relative "message_pack_types/
|
12
|
-
require_relative "message_pack_types/
|
6
|
+
require_relative "message_pack_types/scalars/bigdecimal"
|
7
|
+
require_relative "message_pack_types/scalars/complex"
|
8
|
+
require_relative "message_pack_types/scalars/rational"
|
9
|
+
require_relative "message_pack_types/scalars/date_time"
|
10
|
+
require_relative "message_pack_types/scalars/date"
|
11
|
+
require_relative "message_pack_types/scalars/range"
|
12
|
+
require_relative "message_pack_types/scalars/regexp"
|
13
13
|
|
14
14
|
# composites
|
15
|
-
require_relative "message_pack_types/
|
16
|
-
require_relative "message_pack_types/
|
17
|
-
require_relative "message_pack_types/
|
18
|
-
require_relative "message_pack_types/
|
15
|
+
require_relative "message_pack_types/composites/module"
|
16
|
+
require_relative "message_pack_types/composites/open_struct"
|
17
|
+
require_relative "message_pack_types/composites/struct"
|
18
|
+
require_relative "message_pack_types/composites/set"
|
19
19
|
|
20
20
|
# extensions
|
21
|
-
Dir["#{__dir__}/extensions/**/*.rb"].each { |f| require f }
|
21
|
+
Dir["#{__dir__}/extensions/**/*.rb"].sort.each { |f| require f }
|
22
22
|
|
23
23
|
UniversalID::MessagePackFactory.create_msgpack_pool
|
data/lib/universalid/settings.rb
CHANGED
@@ -39,7 +39,7 @@ class UniversalID::Settings
|
|
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
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 = !!
|
42
|
+
in include_unsaved_changes: include_changes then to.prepack.database.include_changes = !!include_changes # TODO: Remove in v1.0
|
43
43
|
in include_descendants: include_descendants then to.prepack.database.include_descendants = !!include_descendants
|
44
44
|
in descendant_depth: descendant_depth then to.prepack.database.descendant_depth = descendant_depth
|
45
45
|
else # ignore key
|
data/lib/universalid/version.rb
CHANGED
data/lib/uri/uid.rb
CHANGED
@@ -9,6 +9,7 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
9
9
|
VERSION = UniversalID::VERSION
|
10
10
|
SCHEME = "uid"
|
11
11
|
HOST = "universalid"
|
12
|
+
PATTERN = /\A#{SCHEME}:\/\/#{HOST}\/[-_0-9A-Z]+#[-_0-9A-Z]+\z/io
|
12
13
|
|
13
14
|
class << self
|
14
15
|
def encoder
|
@@ -29,6 +30,11 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
29
30
|
new(*::URI.split(value))
|
30
31
|
end
|
31
32
|
|
33
|
+
def match?(uri)
|
34
|
+
return true if uri.is_a?(self)
|
35
|
+
uri.to_s.match? PATTERN
|
36
|
+
end
|
37
|
+
|
32
38
|
def build_string(payload, object = nil)
|
33
39
|
"#{SCHEME}://#{HOST}/#{payload}##{fingerprint(object)}"
|
34
40
|
end
|
@@ -38,6 +44,17 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
38
44
|
parse "#{SCHEME}://#{HOST}#{path}##{fingerprint(object)}"
|
39
45
|
end
|
40
46
|
|
47
|
+
def from_payload(payload, object = nil)
|
48
|
+
parse(build_string(payload, object)).tap do |uid|
|
49
|
+
# NOTE: fingerprint mismatch can happen when building from a UID payload
|
50
|
+
# ensure the fingerprint is correct
|
51
|
+
if uid&.valid? && URI::UID.fingerprint(uid.decode) != uid.fingerprint
|
52
|
+
remove_instance_variable :@decoded_fingerprint if instance_variable_defined?(:@decoded_fingerprint)
|
53
|
+
uid.instance_variable_set :@fragment, URI::UID.build(uid.decode).fingerprint
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
41
58
|
def encode(object, options = {})
|
42
59
|
return yield(object, options) if block_given?
|
43
60
|
encoder.encode object, options
|
@@ -99,7 +116,7 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
99
116
|
end
|
100
117
|
|
101
118
|
def fingerprint(decode: false)
|
102
|
-
return decode_fingerprint if decode
|
119
|
+
return @decoded_fingerprint ||= decode_fingerprint if decode
|
103
120
|
fragment
|
104
121
|
end
|
105
122
|
|
@@ -114,11 +131,14 @@ unless defined?(::URI::UID) || ::URI.scheme_list.include?("UID")
|
|
114
131
|
!valid?
|
115
132
|
end
|
116
133
|
|
117
|
-
def decode
|
134
|
+
def decode(force: false)
|
118
135
|
return nil unless valid?
|
119
|
-
return yield(decode_payload, *decode_fingerprint) if block_given?
|
120
136
|
|
121
|
-
|
137
|
+
remove_instance_variable :@decoded if force && instance_variable_defined?(:@decoded)
|
138
|
+
return @decoded if defined?(@decoded)
|
139
|
+
|
140
|
+
@decoded ||= yield(decode_payload, *decode_fingerprint) if block_given?
|
141
|
+
@decoded ||= decode_payload
|
122
142
|
end
|
123
143
|
|
124
144
|
def deconstruct_keys(_keys)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: universalid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nate Hopkins (hopsoft)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -332,6 +332,20 @@ dependencies:
|
|
332
332
|
- - ">="
|
333
333
|
- !ruby/object:Gem::Version
|
334
334
|
version: '1.32'
|
335
|
+
- !ruby/object:Gem::Dependency
|
336
|
+
name: timecop
|
337
|
+
requirement: !ruby/object:Gem::Requirement
|
338
|
+
requirements:
|
339
|
+
- - ">="
|
340
|
+
- !ruby/object:Gem::Version
|
341
|
+
version: '0'
|
342
|
+
type: :development
|
343
|
+
prerelease: false
|
344
|
+
version_requirements: !ruby/object:Gem::Requirement
|
345
|
+
requirements:
|
346
|
+
- - ">="
|
347
|
+
- !ruby/object:Gem::Version
|
348
|
+
version: '0'
|
335
349
|
description: |
|
336
350
|
Universal ID opens the flood gates with a deluge of profoundly powerful
|
337
351
|
yet easily implemented new use-cases for your apps and scripts.
|
@@ -358,17 +372,17 @@ files:
|
|
358
372
|
- lib/universalid/extensions/signed_global_id/message_pack_type.rb
|
359
373
|
- lib/universalid/message_pack_factory.rb
|
360
374
|
- lib/universalid/message_pack_types.rb
|
361
|
-
- lib/universalid/message_pack_types/
|
362
|
-
- lib/universalid/message_pack_types/
|
363
|
-
- lib/universalid/message_pack_types/
|
364
|
-
- lib/universalid/message_pack_types/
|
365
|
-
- lib/universalid/message_pack_types/
|
366
|
-
- lib/universalid/message_pack_types/
|
367
|
-
- lib/universalid/message_pack_types/
|
368
|
-
- lib/universalid/message_pack_types/
|
369
|
-
- lib/universalid/message_pack_types/
|
370
|
-
- lib/universalid/message_pack_types/
|
371
|
-
- lib/universalid/message_pack_types/
|
375
|
+
- lib/universalid/message_pack_types/composites/module.rb
|
376
|
+
- lib/universalid/message_pack_types/composites/open_struct.rb
|
377
|
+
- lib/universalid/message_pack_types/composites/set.rb
|
378
|
+
- lib/universalid/message_pack_types/composites/struct.rb
|
379
|
+
- lib/universalid/message_pack_types/scalars/bigdecimal.rb
|
380
|
+
- lib/universalid/message_pack_types/scalars/complex.rb
|
381
|
+
- lib/universalid/message_pack_types/scalars/date.rb
|
382
|
+
- lib/universalid/message_pack_types/scalars/date_time.rb
|
383
|
+
- lib/universalid/message_pack_types/scalars/range.rb
|
384
|
+
- lib/universalid/message_pack_types/scalars/rational.rb
|
385
|
+
- lib/universalid/message_pack_types/scalars/regexp.rb
|
372
386
|
- lib/universalid/message_pack_types/uri/uid/type.rb
|
373
387
|
- lib/universalid/packer.rb
|
374
388
|
- lib/universalid/prepack_database_options.rb
|
@@ -404,8 +418,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
404
418
|
- !ruby/object:Gem::Version
|
405
419
|
version: '0'
|
406
420
|
requirements: []
|
407
|
-
rubygems_version: 3.
|
421
|
+
rubygems_version: 3.5.3
|
408
422
|
signing_key:
|
409
423
|
specification_version: 4
|
410
|
-
summary: Fast, recursive,
|
424
|
+
summary: Fast, recursive, optimized, URL-Safe serialization for any Ruby object
|
411
425
|
test_files: []
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|