binary_plist 0.0.1
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 +3 -0
- data/LICENSE +20 -0
- data/Readme.markdown +55 -0
- data/init.rb +11 -0
- data/lib/binary_plist.rb +179 -0
- metadata +85 -0
data/Changelog.markdown
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2010 Sam Soffes
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Readme.markdown
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# BinaryPlist
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add the following line to your bundle and run `bundle install`.
|
|
8
|
+
|
|
9
|
+
gem "binary_plist"
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
All you have to do is add the `bplist` format to your `respond_to` block:
|
|
14
|
+
|
|
15
|
+
def index
|
|
16
|
+
@posts = Post.all
|
|
17
|
+
respond_to do |format|
|
|
18
|
+
format.html
|
|
19
|
+
format.bplist { render :bplist => @posts }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
You can do the combined style if you're support multiple formats like `json` or `xml`.
|
|
24
|
+
|
|
25
|
+
def index
|
|
26
|
+
@posts = Post.all
|
|
27
|
+
respond_to do |format|
|
|
28
|
+
format.html
|
|
29
|
+
format.any(:json, :bplist) { render request.format.to_sym => @posts }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
## Consuming
|
|
34
|
+
|
|
35
|
+
On the Objective-C side, it's ridiculously easy to consume the plist data.
|
|
36
|
+
|
|
37
|
+
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/posts.bplist"];
|
|
38
|
+
NSArray *posts = [NSArray arrayWithContentsOfURL:url];
|
|
39
|
+
|
|
40
|
+
You can also use the more flexible syntax:
|
|
41
|
+
|
|
42
|
+
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/posts.bplist"];
|
|
43
|
+
NSDate *date = [NSData dataWithContentsOfURL:url];
|
|
44
|
+
id result = [NSPropertyListSerialization propertyListFromData:data
|
|
45
|
+
mutabilityOption:NSPropertyListImmutable format:NULL errorDescription:nil];
|
|
46
|
+
|
|
47
|
+
if ([result isKindOfClass:[NSArray class]]) {
|
|
48
|
+
// Handle array response
|
|
49
|
+
} else if ([result isKindOfClass:[NSDictionary class]]) {
|
|
50
|
+
// Handle dictionary response
|
|
51
|
+
} else {
|
|
52
|
+
// Etc...
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Copyright (c) 2010 Sam Soffes, released under the MIT license
|
data/init.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require "binary_plist"
|
|
2
|
+
|
|
3
|
+
Mime::Type.register BinaryPlist::MIME_TYPE, :bplist
|
|
4
|
+
|
|
5
|
+
ActionController::Renderers.add :bplist do |data, options|
|
|
6
|
+
# TODO: Make this less hacky
|
|
7
|
+
data = ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(data, options))
|
|
8
|
+
|
|
9
|
+
self.content_type ||= Mime::BPLIST
|
|
10
|
+
self.response_body = BinaryPlist::Converter.convert(data)
|
|
11
|
+
end
|
data/lib/binary_plist.rb
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Based on Apple Binary Property List serializer - http://gist.github.com/303378
|
|
2
|
+
require 'iconv'
|
|
3
|
+
|
|
4
|
+
module BinaryPlist
|
|
5
|
+
|
|
6
|
+
# Very generic type for now
|
|
7
|
+
MIME_TYPE = 'application/octet-stream'
|
|
8
|
+
|
|
9
|
+
module Converter
|
|
10
|
+
|
|
11
|
+
# Difference between Apple and UNIX timestamps
|
|
12
|
+
DATE_EPOCH_OFFSET_APPLE_UNIX = 978307200
|
|
13
|
+
|
|
14
|
+
# Text encoding
|
|
15
|
+
INPUT_TEXT_ENCODING = 'UTF-8'
|
|
16
|
+
PLIST_TEXT_ENCODING = 'UTF-16BE'
|
|
17
|
+
|
|
18
|
+
# For marking strings as binary data which will be decoded as a CFData object
|
|
19
|
+
CFData = Struct.new(:data)
|
|
20
|
+
|
|
21
|
+
# Convert a Ruby data structure into an OS X binary property list file. (.plist)
|
|
22
|
+
# Works as you'd expect. Integers are limited to 4 bytes, even though the format implies longer values can be written.
|
|
23
|
+
# Strings are assumed to be in UTF-8 format. Symbols are written as strings.
|
|
24
|
+
def self.convert(data)
|
|
25
|
+
write("", data)
|
|
26
|
+
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
|
+
|
|
48
|
+
# How big should the offset ints be?
|
|
49
|
+
# 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.
|
|
50
|
+
offset_format, offset_size = int_format_and_size(offsets.last + values.last.length)
|
|
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
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def self.count_objects(data)
|
|
64
|
+
case data
|
|
65
|
+
when Array
|
|
66
|
+
data.inject(1) { |sum,x| sum + count_objects(x) }
|
|
67
|
+
when Hash
|
|
68
|
+
# Note: Assumes that the keys aren't a Hash or Array
|
|
69
|
+
data.length + count_objects(data.values)
|
|
70
|
+
else
|
|
71
|
+
1
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.append_values(data, values, ref_format)
|
|
76
|
+
case data
|
|
77
|
+
|
|
78
|
+
# Constant values
|
|
79
|
+
when nil
|
|
80
|
+
# values << "\x00"
|
|
81
|
+
# raise "Can't store a nil in a binary plist. While the format supports it, decoders don't like it."
|
|
82
|
+
append_values("", values, ref_format)
|
|
83
|
+
when false
|
|
84
|
+
values << "\x08"
|
|
85
|
+
when true
|
|
86
|
+
values << "\x09"
|
|
87
|
+
|
|
88
|
+
when Integer
|
|
89
|
+
raise "Integer out of range to write in binary plist" if data < -2147483648 || data > 0x7FFFFFFF
|
|
90
|
+
values << packed_int(data)
|
|
91
|
+
|
|
92
|
+
when Float
|
|
93
|
+
values << "\x23#{[data].pack("d").reverse}"
|
|
94
|
+
|
|
95
|
+
when Symbol
|
|
96
|
+
append_values(data.to_s, values, ref_format)
|
|
97
|
+
|
|
98
|
+
when String
|
|
99
|
+
if data =~ /[\x80-\xff]/
|
|
100
|
+
# 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, data).join
|
|
102
|
+
values << "#{objhdr_with_length(0x60, c.length / 2)}#{c}"
|
|
103
|
+
else
|
|
104
|
+
# Just ASCII
|
|
105
|
+
o = objhdr_with_length(0x50, data.length)
|
|
106
|
+
o << data
|
|
107
|
+
values << o
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
when CFData
|
|
111
|
+
o = objhdr_with_length(0x40, data.data.length)
|
|
112
|
+
o << data.data
|
|
113
|
+
values << o
|
|
114
|
+
|
|
115
|
+
when Time
|
|
116
|
+
v = data.getutc.to_f - DATE_EPOCH_OFFSET_APPLE_UNIX
|
|
117
|
+
values << "\x33#{[v].pack("d").reverse}"
|
|
118
|
+
|
|
119
|
+
when Hash
|
|
120
|
+
o = objhdr_with_length(0xd0, data.length)
|
|
121
|
+
values << o # now, so we get the refs of other objects right
|
|
122
|
+
ks = Array.new
|
|
123
|
+
vs = Array.new
|
|
124
|
+
data.each do |k,v|
|
|
125
|
+
ks << values.length
|
|
126
|
+
append_values(k, values, ref_format)
|
|
127
|
+
vs << values.length
|
|
128
|
+
append_values(v, values, ref_format)
|
|
129
|
+
end
|
|
130
|
+
o << ks.pack(ref_format)
|
|
131
|
+
o << vs.pack(ref_format)
|
|
132
|
+
|
|
133
|
+
when Array
|
|
134
|
+
o = objhdr_with_length(0xa0, data.length)
|
|
135
|
+
values << o # now, so we get the refs of other objects right
|
|
136
|
+
refs = Array.new
|
|
137
|
+
data.each do |e|
|
|
138
|
+
refs << values.length # index in array of object we're about to write
|
|
139
|
+
append_values(e, values, ref_format)
|
|
140
|
+
end
|
|
141
|
+
o << refs.pack(ref_format)
|
|
142
|
+
|
|
143
|
+
else
|
|
144
|
+
raise "Couldn't serialize value of class #{data.class.name}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def self.int_format_and_size(i)
|
|
149
|
+
if i > 0xffff
|
|
150
|
+
['N*',4]
|
|
151
|
+
elsif i > 0xff
|
|
152
|
+
['n*',2]
|
|
153
|
+
else
|
|
154
|
+
['C*',1]
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def self.packed_int(data)
|
|
159
|
+
if data < 0
|
|
160
|
+
# Need to use 64 bits for negative numbers.
|
|
161
|
+
[0x13,0xffffffff,data].pack("CNN")
|
|
162
|
+
elsif data > 0xffff
|
|
163
|
+
[0x12,data].pack("CN")
|
|
164
|
+
elsif data > 0xff
|
|
165
|
+
[0x11,data].pack("Cn")
|
|
166
|
+
else
|
|
167
|
+
[0x10,data].pack("CC")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def self.objhdr_with_length(id, length)
|
|
172
|
+
if length < 0xf
|
|
173
|
+
(id + length).chr
|
|
174
|
+
else
|
|
175
|
+
(id + 0xf).chr + packed_int(length)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: binary_plist
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 29
|
|
5
|
+
prerelease: false
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
- 1
|
|
10
|
+
version: 0.0.1
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- Sam Soffes
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2010-07-23 00:00:00 -05:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies:
|
|
21
|
+
- !ruby/object:Gem::Dependency
|
|
22
|
+
name: activesupport
|
|
23
|
+
prerelease: false
|
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
hash: 7
|
|
30
|
+
segments:
|
|
31
|
+
- 3
|
|
32
|
+
- 0
|
|
33
|
+
version: "3.0"
|
|
34
|
+
type: :runtime
|
|
35
|
+
version_requirements: *id001
|
|
36
|
+
description: Easily add the Apple Binary Plist format to your controllers for transferring data to Objective-C applications.
|
|
37
|
+
email: sam@samsoff.es
|
|
38
|
+
executables: []
|
|
39
|
+
|
|
40
|
+
extensions: []
|
|
41
|
+
|
|
42
|
+
extra_rdoc_files: []
|
|
43
|
+
|
|
44
|
+
files:
|
|
45
|
+
- lib/binary_plist.rb
|
|
46
|
+
- Changelog.markdown
|
|
47
|
+
- LICENSE
|
|
48
|
+
- Readme.markdown
|
|
49
|
+
- init.rb
|
|
50
|
+
has_rdoc: true
|
|
51
|
+
homepage: http://github.com/samsoffes/binary_plist
|
|
52
|
+
licenses: []
|
|
53
|
+
|
|
54
|
+
post_install_message:
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
none: false
|
|
61
|
+
requirements:
|
|
62
|
+
- - ">="
|
|
63
|
+
- !ruby/object:Gem::Version
|
|
64
|
+
hash: 3
|
|
65
|
+
segments:
|
|
66
|
+
- 0
|
|
67
|
+
version: "0"
|
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
|
+
none: false
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
hash: 3
|
|
74
|
+
segments:
|
|
75
|
+
- 0
|
|
76
|
+
version: "0"
|
|
77
|
+
requirements: []
|
|
78
|
+
|
|
79
|
+
rubyforge_project:
|
|
80
|
+
rubygems_version: 1.3.7
|
|
81
|
+
signing_key:
|
|
82
|
+
specification_version: 3
|
|
83
|
+
summary: Easily add the Apple Binary Plist format to your controllers.
|
|
84
|
+
test_files: []
|
|
85
|
+
|