binary_plist 0.0.1 → 0.0.3
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.
- data/Changelog.markdown +11 -1
- data/Rakefile +13 -0
- data/Readme.markdown +7 -5
- data/Todo.markdown +8 -0
- data/init.rb +1 -1
- data/lib/binary_plist.rb +99 -104
- metadata +27 -9
data/Changelog.markdown
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
-
0.0.
|
1
|
+
### 0.0.3 (Jul 26, 2010)
|
2
|
+
|
3
|
+
* Fixed ASCII string encoding - closes #1
|
4
|
+
|
5
|
+
### 0.0.2 (Jul 25, 2010)
|
6
|
+
|
7
|
+
* Cleaned up converter
|
8
|
+
* Changed to more standard `.plist` extension
|
9
|
+
* Added basic specs
|
10
|
+
|
11
|
+
### 0.0.1 (Jul 23, 2010)
|
2
12
|
|
3
13
|
* Initial release
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake"
|
3
|
+
require "spec/rake/spectask"
|
4
|
+
|
5
|
+
spec_files = Rake::FileList["spec/*_spec.rb"]
|
6
|
+
|
7
|
+
desc "Run specs"
|
8
|
+
Spec::Rake::SpecTask.new do |t|
|
9
|
+
t.spec_files = spec_files
|
10
|
+
t.spec_opts = ['--color', '--format=specdoc']
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :spec
|
data/Readme.markdown
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Rails plugin for easily adding a binary plist format. The binary plist format is ideal for transferring data to an Objective-C based application.
|
4
4
|
|
5
|
+
**Note:** This is still a work in progress. It should be ready to use for most applications. The most noticeable issue is the lack of support for large integers.
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add the following line to your bundle and run `bundle install`.
|
@@ -10,13 +12,13 @@ Add the following line to your bundle and run `bundle install`.
|
|
10
12
|
|
11
13
|
## Usage
|
12
14
|
|
13
|
-
All you have to do is add the `
|
15
|
+
All you have to do is add the `plist` format to your `respond_to` block:
|
14
16
|
|
15
17
|
def index
|
16
18
|
@posts = Post.all
|
17
19
|
respond_to do |format|
|
18
20
|
format.html
|
19
|
-
format.
|
21
|
+
format.plist { render :plist => @posts }
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
@@ -26,7 +28,7 @@ You can do the combined style if you're support multiple formats like `json` or
|
|
26
28
|
@posts = Post.all
|
27
29
|
respond_to do |format|
|
28
30
|
format.html
|
29
|
-
format.any(:json, :
|
31
|
+
format.any(:json, :plist) { render request.format.to_sym => @posts }
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
@@ -34,12 +36,12 @@ You can do the combined style if you're support multiple formats like `json` or
|
|
34
36
|
|
35
37
|
On the Objective-C side, it's ridiculously easy to consume the plist data.
|
36
38
|
|
37
|
-
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/posts.
|
39
|
+
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/posts.plist"];
|
38
40
|
NSArray *posts = [NSArray arrayWithContentsOfURL:url];
|
39
41
|
|
40
42
|
You can also use the more flexible syntax:
|
41
43
|
|
42
|
-
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/posts.
|
44
|
+
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/posts.plist"];
|
43
45
|
NSDate *date = [NSData dataWithContentsOfURL:url];
|
44
46
|
id result = [NSPropertyListSerialization propertyListFromData:data
|
45
47
|
mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:nil];
|
data/Todo.markdown
ADDED
data/init.rb
CHANGED
@@ -7,5 +7,5 @@ ActionController::Renderers.add :bplist do |data, options|
|
|
7
7
|
data = ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(data, options))
|
8
8
|
|
9
9
|
self.content_type ||= Mime::BPLIST
|
10
|
-
self.response_body = BinaryPlist
|
10
|
+
self.response_body = BinaryPlist.encode(data)
|
11
11
|
end
|
data/lib/binary_plist.rb
CHANGED
@@ -6,122 +6,117 @@ module BinaryPlist
|
|
6
6
|
# Very generic type for now
|
7
7
|
MIME_TYPE = 'application/octet-stream'
|
8
8
|
|
9
|
-
|
9
|
+
# Difference between Apple and UNIX timestamps
|
10
|
+
DATE_EPOCH_OFFSET_APPLE_UNIX = 978307200
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
# Text encoding
|
13
|
+
INPUT_TEXT_ENCODING = 'UTF-8'
|
14
|
+
PLIST_TEXT_ENCODING = 'UTF-16BE'
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
# For marking strings as binary data which will be decoded as a CFData object
|
17
|
+
CFData = Struct.new(:data)
|
18
|
+
|
19
|
+
# Convert a Ruby data structure into a binary property list file.
|
20
|
+
# Works as you'd expect. Integers are limited to 4 bytes, even though the format implies longer values can be written.
|
21
|
+
# Strings are assumed to be in UTF-8 format. Symbols are written as strings.
|
22
|
+
def self.encode object
|
23
|
+
write "", object
|
24
|
+
end
|
25
|
+
|
26
|
+
# Alternative interface which writes data to the out object using <<
|
27
|
+
def self.write out, object
|
28
|
+
# Find out how many objects there are, so we know how big the references are
|
29
|
+
count = count_objects(object)
|
30
|
+
ref_format, ref_size = int_format_and_size(count)
|
17
31
|
|
18
|
-
#
|
19
|
-
|
32
|
+
# Now serialize all the objects
|
33
|
+
values = Array.new
|
34
|
+
append_values(object, values, ref_format)
|
20
35
|
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
36
|
+
# Write header, then the values, calculating offsets as they're written
|
37
|
+
out << 'bplist00'
|
38
|
+
offset = 8
|
39
|
+
offsets = Array.new
|
40
|
+
values.each do |v|
|
41
|
+
offsets << offset
|
42
|
+
out << v
|
43
|
+
offset += v.length
|
26
44
|
end
|
27
|
-
|
28
|
-
# Alternative interface which writes data to the out object using <<
|
29
|
-
def self.write(out, data)
|
30
|
-
# Find out how many objects there are, so we know how big the references are
|
31
|
-
count = count_objects(data)
|
32
|
-
ref_format, ref_size = int_format_and_size(count)
|
33
|
-
|
34
|
-
# Now serialize all the objects
|
35
|
-
values = Array.new
|
36
|
-
append_values(data, values, ref_format)
|
37
|
-
|
38
|
-
# Write header, then the values, calculating offsets as they're written
|
39
|
-
out << 'bplist00'
|
40
|
-
offset = 8
|
41
|
-
offsets = Array.new
|
42
|
-
values.each do |v|
|
43
|
-
offsets << offset
|
44
|
-
out << v
|
45
|
-
offset += v.length
|
46
|
-
end
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# Write the offsets
|
53
|
-
out << offsets.pack(offset_format)
|
54
|
-
|
55
|
-
# Write trailer
|
56
|
-
out << [0,0,offset_size,ref_size, 0,values.length, 0,0, 0,offset].pack("NnCCNNNNNN")
|
57
|
-
|
58
|
-
out
|
59
|
-
end
|
46
|
+
# How big should the offset ints be?
|
47
|
+
# Decoder gets upset if the size can't fit the entire file, even if it's not strictly needed, so add the length of the last value.
|
48
|
+
offset_format, offset_size = int_format_and_size(offsets.last + values.last.length)
|
60
49
|
|
61
|
-
|
50
|
+
# Write the offsets
|
51
|
+
out << offsets.pack(offset_format)
|
62
52
|
|
63
|
-
|
64
|
-
|
53
|
+
# Write trailer
|
54
|
+
out << [0, 0, offset_size, ref_size, 0, values.length, 0, 0, 0, offset].pack("NnCCNNNNNN")
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def self.count_objects object
|
60
|
+
case object
|
65
61
|
when Array
|
66
|
-
|
62
|
+
object.inject(1) { |sum, x| sum + count_objects(x) }
|
63
|
+
|
67
64
|
when Hash
|
68
65
|
# Note: Assumes that the keys aren't a Hash or Array
|
69
|
-
|
66
|
+
object.length + count_objects(object.values)
|
70
67
|
else
|
71
68
|
1
|
72
|
-
end
|
73
69
|
end
|
74
|
-
|
75
|
-
def self.append_values(data, values, ref_format)
|
76
|
-
case data
|
70
|
+
end
|
77
71
|
|
78
|
-
|
72
|
+
def self.append_values object, values, ref_format
|
73
|
+
case object
|
79
74
|
when nil
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
# raise "Can't store a nil in a binary plist. While the format supports it, decoders don't like it." # values << "\x00"
|
76
|
+
# Instead of storing actual nil, store an empty string
|
77
|
+
append_values("", values, ref_format)
|
78
|
+
|
83
79
|
when false
|
84
80
|
values << "\x08"
|
81
|
+
|
85
82
|
when true
|
86
83
|
values << "\x09"
|
87
84
|
|
88
85
|
when Integer
|
89
|
-
raise "Integer out of range to write in binary plist" if
|
90
|
-
values << packed_int(
|
86
|
+
raise "Integer out of range to write in binary plist: #{objet}" if object < -2147483648 || object > 0x7FFFFFFF
|
87
|
+
values << packed_int(object)
|
91
88
|
|
92
89
|
when Float
|
93
|
-
values << "\x23#{[
|
90
|
+
values << "\x23#{[object].pack("d").reverse}"
|
94
91
|
|
95
92
|
when Symbol
|
96
|
-
append_values(
|
93
|
+
append_values(object.to_s, values, ref_format)
|
97
94
|
|
98
95
|
when String
|
99
|
-
if
|
96
|
+
if object =~ /[\x80-\xff]/
|
100
97
|
# Has high bits set, so is UTF-8 and must be reencoded for the plist file
|
101
|
-
c = Iconv.iconv(PLIST_TEXT_ENCODING, INPUT_TEXT_ENCODING,
|
102
|
-
values <<
|
98
|
+
c = Iconv.iconv(PLIST_TEXT_ENCODING, INPUT_TEXT_ENCODING, object).join
|
99
|
+
values << objhdr_with_length(0x60, c.length / 2) + c
|
103
100
|
else
|
104
101
|
# Just ASCII
|
105
|
-
|
106
|
-
o << data
|
107
|
-
values << o
|
102
|
+
values << objhdr_with_length(0x50, object.length) + object
|
108
103
|
end
|
109
104
|
|
110
105
|
when CFData
|
111
|
-
o = objhdr_with_length(0x40,
|
112
|
-
o <<
|
106
|
+
o = objhdr_with_length(0x40, object.data.length)
|
107
|
+
o << object.data
|
113
108
|
values << o
|
114
109
|
|
115
110
|
when Time
|
116
|
-
v =
|
111
|
+
v = object.getutc.to_f - DATE_EPOCH_OFFSET_APPLE_UNIX
|
117
112
|
values << "\x33#{[v].pack("d").reverse}"
|
118
113
|
|
119
114
|
when Hash
|
120
|
-
o = objhdr_with_length(0xd0,
|
115
|
+
o = objhdr_with_length(0xd0, object.length)
|
121
116
|
values << o # now, so we get the refs of other objects right
|
122
117
|
ks = Array.new
|
123
118
|
vs = Array.new
|
124
|
-
|
119
|
+
object.each do |k,v|
|
125
120
|
ks << values.length
|
126
121
|
append_values(k, values, ref_format)
|
127
122
|
vs << values.length
|
@@ -129,12 +124,13 @@ module BinaryPlist
|
|
129
124
|
end
|
130
125
|
o << ks.pack(ref_format)
|
131
126
|
o << vs.pack(ref_format)
|
127
|
+
|
132
128
|
|
133
129
|
when Array
|
134
|
-
o = objhdr_with_length(0xa0,
|
130
|
+
o = objhdr_with_length(0xa0, object.length)
|
135
131
|
values << o # now, so we get the refs of other objects right
|
136
132
|
refs = Array.new
|
137
|
-
|
133
|
+
object.each do |e|
|
138
134
|
refs << values.length # index in array of object we're about to write
|
139
135
|
append_values(e, values, ref_format)
|
140
136
|
end
|
@@ -142,38 +138,37 @@ module BinaryPlist
|
|
142
138
|
|
143
139
|
else
|
144
140
|
raise "Couldn't serialize value of class #{data.class.name}"
|
145
|
-
end
|
146
141
|
end
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.int_format_and_size(i)
|
145
|
+
if i > 0xffff
|
146
|
+
['N*', 4]
|
147
|
+
elsif i > 0xff
|
148
|
+
['n*', 2]
|
149
|
+
else
|
150
|
+
['C*', 1]
|
156
151
|
end
|
152
|
+
end
|
157
153
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
end
|
154
|
+
def self.packed_int integer
|
155
|
+
if integer < 0
|
156
|
+
# Need to use 64 bits for negative numbers.
|
157
|
+
[0x13, 0xffffffff, integer].pack("CNN")
|
158
|
+
elsif integer > 0xffff
|
159
|
+
[0x12, integer].pack("CN")
|
160
|
+
elsif integer > 0xff
|
161
|
+
[0x11, integer].pack("Cn")
|
162
|
+
else
|
163
|
+
[0x10, integer].pack("CC")
|
169
164
|
end
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.objhdr_with_length id, length
|
168
|
+
if length < 0xf
|
169
|
+
(id + length).chr
|
170
|
+
else
|
171
|
+
(id + 0xf).chr + packed_int(length)
|
177
172
|
end
|
178
|
-
|
173
|
+
end
|
179
174
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: binary_plist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sam Soffes
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-07-
|
18
|
+
date: 2010-07-26 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -26,14 +26,30 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 3
|
30
30
|
segments:
|
31
|
-
-
|
31
|
+
- 2
|
32
32
|
- 0
|
33
|
-
version: "
|
33
|
+
version: "2.0"
|
34
34
|
type: :runtime
|
35
35
|
version_requirements: *id001
|
36
|
-
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - "="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 27
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 3
|
48
|
+
- 0
|
49
|
+
version: 1.3.0
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
description: Easily convert Ruby objects to the Apple Binary Plist format for transferring data to Objective-C applications.
|
37
53
|
email: sam@samsoff.es
|
38
54
|
executables: []
|
39
55
|
|
@@ -45,7 +61,9 @@ files:
|
|
45
61
|
- lib/binary_plist.rb
|
46
62
|
- Changelog.markdown
|
47
63
|
- LICENSE
|
64
|
+
- Rakefile
|
48
65
|
- Readme.markdown
|
66
|
+
- Todo.markdown
|
49
67
|
- init.rb
|
50
68
|
has_rdoc: true
|
51
69
|
homepage: http://github.com/samsoffes/binary_plist
|
@@ -80,6 +98,6 @@ rubyforge_project:
|
|
80
98
|
rubygems_version: 1.3.7
|
81
99
|
signing_key:
|
82
100
|
specification_version: 3
|
83
|
-
summary: Easily
|
101
|
+
summary: Easily convert Ruby objects to the Apple Binary Plist format.
|
84
102
|
test_files: []
|
85
103
|
|