keyed_archive 0.1.0 → 1.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.
- checksums.yaml +5 -5
- data/README.md +27 -9
- data/keyed_archive.gemspec +3 -3
- data/lib/keyed_archive.rb +22 -9
- data/lib/keyed_archive/unpacked_objects.rb +67 -23
- data/lib/keyed_archive/version.rb +1 -1
- data/spec/fixtures/empty.bplist +0 -0
- data/spec/fixtures/single_object_reference.bplist +0 -0
- data/spec/keyed_archive_spec.rb +11 -5
- data/spec/unpacked_objects_spec.rb +13 -4
- metadata +17 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d932ce3e1c5bf7832937aeba3b66736cc94b9036ae9a242e32ef3d4310cece72
|
4
|
+
data.tar.gz: 74e5020a25d5346e1789fd12d4e4038c00f64c99a9a44f8906bbfa47d677f879
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a0152d78b35fd324d3c48838ce21c19d71530e5d0234dabe668dd89e5dd88fae5f51539dbf6a62162a52fd4fabf1ac4e7ecba7540170acf277c75d5122e53d5
|
7
|
+
data.tar.gz: 5051e71618ab45823bd96fa1e1e00ec0d33c5311af024da0a1e2ec14e31115570b523324c6f64f060bf434da3559c864de0df2f72572b64c81a124280ea3daa6
|
data/README.md
CHANGED
@@ -18,6 +18,22 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
+
### Basic Syntax
|
22
|
+
|
23
|
+
KeyedArchive supports the initializer arguments which CFPropertyList supports: `:file` to read data directly from a file and `:data` to read data from a variable that already has your NSKeyedArchive data within it.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
|
27
|
+
# Create KeyedArchive by reading a file directly
|
28
|
+
keyed_archive_from_file = KeyedArchive.new(:file => "path/to/file.plist")
|
29
|
+
|
30
|
+
# Create KeyedArchive from a variable holding content which can
|
31
|
+
# be parsed as a plist, either XML or binary.
|
32
|
+
file_contents = File.read("path/to/file.plist")
|
33
|
+
keyed_archive_from_variable = KeyedArchive.new(:data => file_contents)
|
34
|
+
|
35
|
+
```
|
36
|
+
|
21
37
|
### Opening a keyed archive
|
22
38
|
```xml
|
23
39
|
<?xml version="1.0" encoding="UTF-8"?>
|
@@ -48,11 +64,12 @@ Or install it yourself as:
|
|
48
64
|
require 'keyed_archive'
|
49
65
|
|
50
66
|
filename = "path/to/file.plist"
|
51
|
-
keyed_archive = KeyedArchive.new("path/to/file.plist")
|
52
|
-
keyed_archive.archiver
|
53
|
-
keyed_archive.objects
|
54
|
-
keyed_archive.top
|
55
|
-
keyed_archive.version
|
67
|
+
keyed_archive = KeyedArchive.new(:file => "path/to/file.plist")
|
68
|
+
keyed_archive.archiver # "NSKeyedArchiver"
|
69
|
+
keyed_archive.objects # ["$null"]
|
70
|
+
keyed_archive.top # {"root"=>1}
|
71
|
+
keyed_archive.version # 100000
|
72
|
+
keyed_archive.unpacked_top # {"root"=>nil}
|
56
73
|
```
|
57
74
|
|
58
75
|
### Unpacking keyed archive objects
|
@@ -88,14 +105,15 @@ keyed_archive.version # 100000
|
|
88
105
|
```ruby
|
89
106
|
require 'keyed_archive'
|
90
107
|
|
91
|
-
keyed_archive = KeyedArchive.new("path/to/file.plist")
|
92
|
-
keyed_archive.objects # ["$null",
|
93
|
-
keyed_archive.
|
108
|
+
keyed_archive = KeyedArchive.new(:file => "path/to/file.plist")
|
109
|
+
keyed_archive.objects # ["$null", {"key"=>"value"}]
|
110
|
+
keyed_archive.top # {"root"=>1}
|
111
|
+
keyed_archive.unpacked_top() # {"root"=>{"key"=>"value"}}
|
94
112
|
```
|
95
113
|
|
96
114
|
## Contributing
|
97
115
|
|
98
|
-
1. Fork it ( http://github.com
|
116
|
+
1. Fork it ( http://github.com/\<my-github-username\>/keyed_archive/fork )
|
99
117
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
100
118
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
101
119
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/keyed_archive.gemspec
CHANGED
@@ -17,9 +17,9 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency "CFPropertyList", "~>
|
20
|
+
spec.add_dependency "CFPropertyList", "~> 3.0"
|
21
21
|
|
22
|
-
spec.add_development_dependency "bundler", "~> 1
|
22
|
+
spec.add_development_dependency "bundler", "~> 2.1"
|
23
23
|
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "rspec", "~>
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.9"
|
25
25
|
end
|
data/lib/keyed_archive.rb
CHANGED
@@ -1,18 +1,31 @@
|
|
1
1
|
require 'cfpropertylist'
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'keyed_archive/unpacked_objects'
|
4
|
+
require 'keyed_archive/version'
|
5
5
|
|
6
6
|
class KeyedArchive
|
7
7
|
attr_accessor :archiver, :objects, :top, :version
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
#
|
10
|
+
# Take the same sort of arguments as CFPropertyList
|
11
|
+
# :file => filename to load
|
12
|
+
# :data => variable with the data to load directly
|
13
|
+
def initialize(opts={})
|
14
|
+
blob = opts[:data]
|
15
|
+
filename = opts[:file]
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
plist = CFPropertyList::List.new(:file => filename) unless filename.nil? or !File.exists?(filename)
|
18
|
+
plist = CFPropertyList::List.new(:data => blob) unless blob.nil? or blob.length < 1
|
19
|
+
|
20
|
+
if !plist.nil?
|
21
|
+
data = CFPropertyList.native_types(plist.value)
|
22
|
+
|
23
|
+
@archiver = data['$archiver']
|
24
|
+
@objects = data['$objects']
|
25
|
+
@top = data['$top']
|
26
|
+
@version = data['$version']
|
27
|
+
else
|
28
|
+
raise "Plist not created"
|
29
|
+
end
|
17
30
|
end
|
18
31
|
end
|
@@ -1,31 +1,75 @@
|
|
1
1
|
class KeyedArchive
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
2
|
+
|
3
|
+
# Loops through the entries within '$top'
|
4
|
+
# to replace any values that are pointers to objects.
|
5
|
+
def unpacked_top
|
6
|
+
|
7
|
+
# Create the return value
|
8
|
+
unpacked_top = Hash.new
|
9
|
+
|
10
|
+
# Loop over each pair in the '$top' hash
|
11
|
+
# to recursively replace the values
|
12
|
+
@top.each_pair do |key, value|
|
13
|
+
unpacked_top[key] = recursive_replace(value, -1, [])
|
14
|
+
end
|
15
|
+
|
16
|
+
# Politely return, our job is done
|
17
|
+
return unpacked_top
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# Handles the recursive replacement of values within the
|
23
|
+
# '$objects' array. Tracks the object locations it has
|
24
|
+
# already touched to prevent infinite loops.
|
25
|
+
def recursive_replace(value, current_location, locations)
|
26
|
+
# By default we just return the value itself, usually a String
|
27
|
+
to_return = value
|
28
|
+
|
29
|
+
# If the value is really representing nil, change it to nil
|
30
|
+
if value.is_a? String and value == "$null"
|
31
|
+
to_return = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
# If we have an Integer, we want to bring in the object
|
35
|
+
# that Integer points to
|
36
|
+
if value.is_a? Integer
|
37
|
+
|
38
|
+
# If we ever find a reference to one of our parents, just stop where we are
|
39
|
+
if locations.include?(current_location)
|
40
|
+
to_return = value
|
41
|
+
else
|
42
|
+
to_return = recursive_replace(@objects[value], value, locations.clone.push(current_location))
|
43
|
+
end
|
44
|
+
|
45
|
+
# If we have a Hash, we want to check each entry
|
46
|
+
# and replace any values which need it
|
47
|
+
elsif value.is_a? Hash
|
48
|
+
|
49
|
+
# Build up a new Hash
|
50
|
+
to_return = Hash.new
|
51
|
+
|
52
|
+
# Loop over the pairs to check the key, value, or both
|
53
|
+
value.each_pair do |tmp_key, tmp_value|
|
54
|
+
|
55
|
+
# If this points to an array of objects,
|
56
|
+
# then we should bring those values in
|
57
|
+
if tmp_key == "NS.objects" or tmp_key == "NS.keys"
|
58
|
+
new_array = Array.new
|
59
|
+
tmp_value.each do |entry|
|
60
|
+
new_array.push(recursive_replace(entry, entry, locations.clone.push(current_location)))
|
22
61
|
end
|
62
|
+
to_return[tmp_key] = new_array
|
63
|
+
|
64
|
+
# Otherwise, we just want to replace the value with
|
65
|
+
# its recursive version
|
66
|
+
else
|
67
|
+
to_return[tmp_key] = recursive_replace(tmp_value, tmp_value, locations.clone.push(current_location))
|
23
68
|
end
|
24
|
-
else
|
25
|
-
unpacked_objects.push object
|
26
69
|
end
|
27
70
|
end
|
28
71
|
|
29
|
-
|
72
|
+
# Give back the value, politely
|
73
|
+
return to_return
|
30
74
|
end
|
31
75
|
end
|
Binary file
|
Binary file
|
data/spec/keyed_archive_spec.rb
CHANGED
@@ -2,29 +2,35 @@ require "keyed_archive"
|
|
2
2
|
|
3
3
|
describe KeyedArchive, "#initialize" do
|
4
4
|
let(:filename) { 'spec/fixtures/empty.plist' }
|
5
|
+
let(:blob) { File.read('spec/fixtures/empty.bplist') }
|
5
6
|
|
6
7
|
it "should create an instance" do
|
7
|
-
keyed_archive = KeyedArchive.new(filename)
|
8
|
+
keyed_archive = KeyedArchive.new(:file => filename)
|
8
9
|
expect(keyed_archive).to be_instance_of(KeyedArchive)
|
9
10
|
end
|
10
11
|
|
11
12
|
it "should set the archiver" do
|
12
|
-
keyed_archive = KeyedArchive.new(filename)
|
13
|
+
keyed_archive = KeyedArchive.new(:file => filename)
|
13
14
|
expect(keyed_archive.archiver).to be_a(String)
|
14
15
|
end
|
15
16
|
|
16
17
|
it "should set the objects" do
|
17
|
-
keyed_archive = KeyedArchive.new(filename)
|
18
|
+
keyed_archive = KeyedArchive.new(:file => filename)
|
18
19
|
expect(keyed_archive.objects).to be_an(Array)
|
19
20
|
end
|
20
21
|
|
21
22
|
it "should set the top" do
|
22
|
-
keyed_archive = KeyedArchive.new(filename)
|
23
|
+
keyed_archive = KeyedArchive.new(:file => filename)
|
23
24
|
expect(keyed_archive.top).to be_a(Hash)
|
24
25
|
end
|
25
26
|
|
26
27
|
it "should set the version" do
|
27
|
-
keyed_archive = KeyedArchive.new(filename)
|
28
|
+
keyed_archive = KeyedArchive.new(:file => filename)
|
29
|
+
expect(keyed_archive.version).to be_an(Integer)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should read data from variables as well" do
|
33
|
+
keyed_archive = KeyedArchive.new(:data => blob)
|
28
34
|
expect(keyed_archive.version).to be_an(Integer)
|
29
35
|
end
|
30
36
|
end
|
@@ -2,10 +2,17 @@ require "keyed_archive/unpacked_objects"
|
|
2
2
|
|
3
3
|
shared_examples "unpack" do
|
4
4
|
it "should unpack objects" do
|
5
|
-
keyed_archive = KeyedArchive.new(filename)
|
6
|
-
|
7
|
-
fixture_archive = KeyedArchive.new(fixture)
|
8
|
-
expect(
|
5
|
+
keyed_archive = KeyedArchive.new(:file => filename)
|
6
|
+
unpacked_top = keyed_archive.unpacked_top()
|
7
|
+
fixture_archive = KeyedArchive.new(:file => fixture)
|
8
|
+
expect(unpacked_top["root"]).to eq(fixture_archive.objects[1]["key"])
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should handle variables" do
|
12
|
+
keyed_archive = KeyedArchive.new(:data => blob)
|
13
|
+
unpacked_top = keyed_archive.unpacked_top()
|
14
|
+
fixture_archive = KeyedArchive.new(:file => fixture)
|
15
|
+
expect(unpacked_top["root"]).to eq(fixture_archive.objects[1]["key"])
|
9
16
|
end
|
10
17
|
end
|
11
18
|
|
@@ -13,6 +20,7 @@ describe KeyedArchive, "#unpack" do
|
|
13
20
|
context "single reference" do
|
14
21
|
it_behaves_like "unpack" do
|
15
22
|
let(:filename) { "spec/fixtures/single_object_reference.plist" }
|
23
|
+
let(:blob) { File.read("spec/fixtures/single_object_reference.plist") }
|
16
24
|
let(:fixture) { "spec/fixtures/single_object_reference_unpacked.plist" }
|
17
25
|
end
|
18
26
|
end
|
@@ -20,6 +28,7 @@ describe KeyedArchive, "#unpack" do
|
|
20
28
|
context "double reference" do
|
21
29
|
it_behaves_like "unpack" do
|
22
30
|
let(:filename) { "spec/fixtures/double_object_reference.plist" }
|
31
|
+
let(:blob) { File.read("spec/fixtures/double_object_reference.plist") }
|
23
32
|
let(:fixture) { "spec/fixtures/double_object_reference_unpacked.plist" }
|
24
33
|
end
|
25
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keyed_archive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Young
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: CFPropertyList
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '3.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '3.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1
|
33
|
+
version: '2.1'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1
|
40
|
+
version: '2.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,15 +58,15 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: '3.9'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
69
|
-
description:
|
68
|
+
version: '3.9'
|
69
|
+
description:
|
70
70
|
email:
|
71
71
|
- paulyoungonline@gmail.com
|
72
72
|
executables: []
|
@@ -85,7 +85,9 @@ files:
|
|
85
85
|
- lib/keyed_archive/version.rb
|
86
86
|
- spec/fixtures/double_object_reference.plist
|
87
87
|
- spec/fixtures/double_object_reference_unpacked.plist
|
88
|
+
- spec/fixtures/empty.bplist
|
88
89
|
- spec/fixtures/empty.plist
|
90
|
+
- spec/fixtures/single_object_reference.bplist
|
89
91
|
- spec/fixtures/single_object_reference.plist
|
90
92
|
- spec/fixtures/single_object_reference_unpacked.plist
|
91
93
|
- spec/keyed_archive_spec.rb
|
@@ -94,7 +96,7 @@ homepage: https://github.com/paulyoung/keyed_archive
|
|
94
96
|
licenses:
|
95
97
|
- MIT
|
96
98
|
metadata: {}
|
97
|
-
post_install_message:
|
99
|
+
post_install_message:
|
98
100
|
rdoc_options: []
|
99
101
|
require_paths:
|
100
102
|
- lib
|
@@ -109,15 +111,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
111
|
- !ruby/object:Gem::Version
|
110
112
|
version: '0'
|
111
113
|
requirements: []
|
112
|
-
|
113
|
-
|
114
|
-
signing_key:
|
114
|
+
rubygems_version: 3.1.2
|
115
|
+
signing_key:
|
115
116
|
specification_version: 4
|
116
117
|
summary: A Ruby gem for working with files produced by NSKeyedArchiver.
|
117
118
|
test_files:
|
118
119
|
- spec/fixtures/double_object_reference.plist
|
119
120
|
- spec/fixtures/double_object_reference_unpacked.plist
|
121
|
+
- spec/fixtures/empty.bplist
|
120
122
|
- spec/fixtures/empty.plist
|
123
|
+
- spec/fixtures/single_object_reference.bplist
|
121
124
|
- spec/fixtures/single_object_reference.plist
|
122
125
|
- spec/fixtures/single_object_reference_unpacked.plist
|
123
126
|
- spec/keyed_archive_spec.rb
|