beefcake-spanx 0.3.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in beefcake.gemspec
4
+ gemspec
@@ -0,0 +1,16 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ beefcake-spanx (0.3.4.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ rake (0.9.2)
10
+
11
+ PLATFORMS
12
+ ruby
13
+
14
+ DEPENDENCIES
15
+ beefcake-spanx!
16
+ rake
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2011 Blake Mizerany
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,116 @@
1
+ # Beefcake (A sane Google Protocol Buffers library for Ruby)
2
+ ## It's all about being Buf; ProtoBuf.
3
+
4
+ # Install
5
+
6
+ $ gem install beefcake
7
+
8
+ # Example
9
+
10
+ require 'beefcake'
11
+
12
+ class Variety
13
+ include Beefcake::Message
14
+
15
+ # Required
16
+ required :x, :int32, 1
17
+ required :y, :int32, 2
18
+
19
+ # Optional
20
+ optional :tag, :string, 3
21
+
22
+ # Repeated
23
+ repeated :ary, :fixed64, 4
24
+ repeated :pary, :fixed64, 5, :packed => true
25
+
26
+ # Enums - Simply use a Module (NOTE: defaults are optional)
27
+ module Foonum
28
+ A = 1
29
+ B = 2
30
+ end
31
+
32
+ # As per the spec, defaults are only set at the end
33
+ # of decoding a message, not on object creation.
34
+ optional :foo, Foonum, 6, :default => Foonum::B
35
+ end
36
+
37
+ x = Variety.new :x => 1, :y => 2
38
+ # or
39
+ x = Variety.new
40
+ x.x = 1
41
+ x.y = 2
42
+
43
+ ## Encoding
44
+
45
+ Any object responding to `<<` can accept encoding
46
+
47
+ s = ""
48
+ x.encode(s)
49
+ p [:s, s]
50
+ # or (because encode returns the string/stream)
51
+ p [:s, x.encode]
52
+ # or
53
+ open("x.dat") do |f|
54
+ x.encode(f)
55
+ end
56
+
57
+ # decode
58
+ encoded = x.encode
59
+ decoded = Variety.decode(encoded)
60
+ p [:x, decoded]
61
+
62
+ # decode merge
63
+ Variety.decoded(more_data, decoded)
64
+
65
+ # Why?
66
+
67
+ Ruby deserves and needs first-class ProtoBuf support.
68
+ Other libs didn't feel very "Ruby" to me and were hard to parse.
69
+
70
+ # Generate code from `.proto` file
71
+
72
+ $ protoc --beefcake_out output/path -I path/to/proto/files/dir path/to/proto/file
73
+
74
+ You can set the BEEFCAKE_NAMESPACE variable to generate the classes under a
75
+ desired namespace. (i.e. App::Foo::Bar)
76
+
77
+ # Misc
78
+
79
+ This library was built with EventMachine in mind. Not just blocking-IO.
80
+
81
+ # Dev
82
+
83
+ Source:
84
+
85
+ $ git clone git://github.com/bmizerany/beefcake
86
+
87
+ ## Testing:
88
+
89
+ $ gem install turn
90
+ $ cd /path/to/beefcake
91
+ $ turn
92
+
93
+ ## VMs:
94
+
95
+ Currently Beefcake is tested and working on:
96
+
97
+ * Ruby 1.8.6
98
+ * Ruby 1.8.7
99
+ * Ruby 1.9.2
100
+ * JRuby 1.5.6
101
+ * Rubinius edge
102
+
103
+ ## Future
104
+
105
+ Nice to have:
106
+
107
+ * Groups (would be nice for accessing older protos)
108
+
109
+ # Further Reading
110
+
111
+ http://code.google.com/apis/protocolbuffers/docs/encoding.html
112
+
113
+ # Thank You
114
+
115
+ Keith Rarick (kr) for help with encoding/decoding.
116
+ Aman Gupta (tmm1) for help with cross VM support and performance enhancements.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "beefcake/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "beefcake-spanx"
7
+ s.version = Beefcake::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Blake Mizerany"]
10
+ s.email = ["blake.mizerany@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{A sane protobuf library for Ruby}
13
+ s.description = %q{A sane protobuf library for Ruby}
14
+
15
+ s.rubyforge_project = "beefcake"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ s.add_development_dependency "rake"
22
+ end
@@ -0,0 +1,90 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'beefcake'
3
+
4
+ class MyMessage
5
+ include Beefcake::Message
6
+ required :number, :int32, 1
7
+ required :chars, :string, 2
8
+ required :raw, :bytes, 3
9
+ required :bool, :bool, 4
10
+ required :float, :float, 5
11
+ end
12
+
13
+ if ARGV[0] == 'pprof'
14
+ # profile message creation/encoding/decoding w/ perftools.rb
15
+ # works on 1.8 and 1.9
16
+ # ruby bench/simple.rb pprof
17
+ # open bench/beefcake.prof.gif
18
+
19
+ ENV['CPUPROFILE_FREQUENCY'] = '4000'
20
+ require 'rubygems'
21
+ require 'perftools'
22
+ PerfTools::CpuProfiler.start(File.expand_path("../beefcake.prof", __FILE__)) do
23
+ 100_000.times do
24
+ str = MyMessage.new(
25
+ :number => 12345,
26
+ :chars => 'hello',
27
+ :raw => 'world',
28
+ :bool => true,
29
+ :float => 1.2345
30
+ ).encode
31
+ MyMessage.decode(str)
32
+ end
33
+ end
34
+ Dir.chdir(File.dirname(__FILE__)) do
35
+ `pprof.rb beefcake.prof --gif > beefcake.prof.gif`
36
+ end
37
+
38
+ else
39
+ # benchmark message creation/encoding/decoding
40
+ # rvm install 1.8.7 1.9.2 jruby rbx
41
+ # rvm 1.8.7,1.9.2,jruby,rbx ruby bench/simple.rb
42
+
43
+ require 'benchmark'
44
+
45
+ ITERS = 100_000
46
+
47
+ Benchmark.bmbm do |x|
48
+ x.report 'object creation' do
49
+ ITERS.times do
50
+ Object.new
51
+ end
52
+ end
53
+ x.report 'message creation' do
54
+ ITERS.times do
55
+ MyMessage.new(
56
+ :number => 12345,
57
+ :chars => 'hello',
58
+ :raw => 'world',
59
+ :bool => true,
60
+ :float => 1.2345
61
+ )
62
+ end
63
+ end
64
+ x.report 'message encoding' do
65
+ m = MyMessage.new(
66
+ :number => 12345,
67
+ :chars => 'hello',
68
+ :raw => 'world',
69
+ :bool => true,
70
+ :float => 1.2345
71
+ )
72
+ ITERS.times do
73
+ m.encode
74
+ end
75
+ end
76
+ x.report 'message decoding' do
77
+ str = MyMessage.new(
78
+ :number => 12345,
79
+ :chars => 'hello',
80
+ :raw => 'world',
81
+ :bool => true,
82
+ :float => 1.2345
83
+ ).encode.to_s
84
+ ITERS.times do
85
+ MyMessage.decode(str.dup)
86
+ end
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'beefcake/generator'
4
+
5
+ ns = (ENV["BEEFCAKE_NAMESPACE"] || "").split("::")
6
+
7
+ req = CodeGeneratorRequest.decode(STDIN.read)
8
+ res = Beefcake::Generator.compile(ns, req)
9
+
10
+ # Send it out!
11
+ STDOUT.print(res.encode)
@@ -0,0 +1,246 @@
1
+ require 'beefcake/buffer'
2
+
3
+ module Beefcake
4
+ module Message
5
+
6
+ class WrongTypeError < StandardError
7
+ def initialize(name, exp, got)
8
+ super("Wrong type `#{got}` given for (#{name}). Expected #{exp}")
9
+ end
10
+ end
11
+
12
+
13
+ class InvalidValueError < StandardError
14
+ def initialize(name, val)
15
+ super("Invalid Value given for `#{name}`: #{val.inspect}")
16
+ end
17
+ end
18
+
19
+
20
+ class RequiredFieldNotSetError < StandardError
21
+ def initialize(name)
22
+ super("Field #{name} is required but nil")
23
+ end
24
+ end
25
+
26
+
27
+ class Field < Struct.new(:rule, :name, :type, :fn, :opts)
28
+ def <=>(o)
29
+ fn <=> o.fn
30
+ end
31
+ end
32
+
33
+
34
+ module Dsl
35
+ def required(name, type, fn, opts={})
36
+ field(:required, name, type, fn, opts)
37
+ end
38
+
39
+ def repeated(name, type, fn, opts={})
40
+ field(:repeated, name, type, fn, opts)
41
+ end
42
+
43
+ def optional(name, type, fn, opts={})
44
+ field(:optional, name, type, fn, opts)
45
+ end
46
+
47
+ def field(rule, name, type, fn, opts)
48
+ fields[fn] = Field.new(rule, name, type, fn, opts)
49
+ attr_accessor name
50
+ end
51
+
52
+ def fields
53
+ @fields ||= {}
54
+ end
55
+ end
56
+
57
+ module Encode
58
+
59
+ def encode(buf = Buffer.new)
60
+ validate!
61
+
62
+ if ! buf.respond_to?(:<<)
63
+ raise ArgumentError, "buf doesn't respond to `<<`"
64
+ end
65
+
66
+ if ! buf.is_a?(Buffer)
67
+ buf = Buffer.new(buf)
68
+ end
69
+
70
+ # TODO: Error if any required fields at nil
71
+
72
+ fields.values.sort.each do |fld|
73
+ if fld.opts[:packed]
74
+ bytes = encode!(Buffer.new, fld, 0)
75
+ buf.append_info(fld.fn, Buffer.wire_for(fld.type))
76
+ buf.append_uint64(bytes.length)
77
+ buf << bytes
78
+ else
79
+ encode!(buf, fld, fld.fn)
80
+ end
81
+ end
82
+
83
+ buf
84
+ end
85
+
86
+ def encode!(buf, fld, fn)
87
+ v = self[fld.name]
88
+ v = v.is_a?(Array) ? v : [v]
89
+
90
+ v.compact.each do |val|
91
+ case fld.type
92
+ when Class # encodable
93
+ # TODO: raise error if type != val.class
94
+ buf.append(:string, val.encode, fn)
95
+ when Module # enum
96
+ if ! valid_enum?(fld.type, val)
97
+ raise InvalidValueError.new(fld.name, val)
98
+ end
99
+
100
+ buf.append(:int32, val, fn)
101
+ else
102
+ buf.append(fld.type, val, fn)
103
+ end
104
+ end
105
+
106
+ buf
107
+ end
108
+
109
+ def valid_enum?(mod, val)
110
+ !!name_for(mod, val)
111
+ end
112
+
113
+ def name_for(mod, val)
114
+ mod.constants.each do |name|
115
+ if mod.const_get(name) == val
116
+ return name
117
+ end
118
+ end
119
+ nil
120
+ end
121
+
122
+ def validate!
123
+ fields.values.each do |fld|
124
+ if fld.rule == :required && self[fld.name].nil?
125
+ raise RequiredFieldNotSetError, fld.name
126
+ end
127
+ end
128
+ end
129
+
130
+ end
131
+
132
+
133
+ module Decode
134
+ def decode(buf, o=self.new)
135
+ if ! buf.is_a?(Buffer)
136
+ buf = Buffer.new(buf)
137
+ end
138
+
139
+ # TODO: test for incomplete buffer
140
+ while buf.length > 0
141
+ fn, wire = buf.read_info
142
+
143
+ fld = fields[fn]
144
+
145
+ # We don't have a field for with index fn.
146
+ # Ignore this data and move on.
147
+ if fld.nil?
148
+ buf.skip(wire)
149
+ next
150
+ end
151
+
152
+ exp = Buffer.wire_for(fld.type)
153
+ if wire != exp
154
+ raise WrongTypeError.new(fld.name, exp, wire)
155
+ end
156
+
157
+ if fld.rule == :repeated && fld.opts[:packed]
158
+ len = buf.read_uint64
159
+ tmp = Buffer.new(buf.read(len))
160
+ o[fld.name] ||= []
161
+ while tmp.length > 0
162
+ o[fld.name] << tmp.read(fld.type)
163
+ end
164
+ elsif fld.rule == :repeated
165
+ val = buf.read(fld.type)
166
+ (o[fld.name] ||= []) << val
167
+ else
168
+ val = buf.read(fld.type)
169
+ o[fld.name] = val
170
+ end
171
+ end
172
+
173
+ # Set defaults
174
+ fields.values.each do |f|
175
+ next if o[f.name] == false
176
+ o[f.name] ||= f.opts[:default]
177
+ end
178
+
179
+ o.validate!
180
+
181
+ o
182
+ end
183
+ end
184
+
185
+
186
+ def self.included(o)
187
+ o.extend Dsl
188
+ o.extend Decode
189
+ o.send(:include, Encode)
190
+ end
191
+
192
+ def initialize(attrs={})
193
+ fields.values.each do |fld|
194
+ self[fld.name] = attrs[fld.name]
195
+ end
196
+ end
197
+
198
+ def fields
199
+ self.class.fields
200
+ end
201
+
202
+ def [](k)
203
+ __send__(k)
204
+ end
205
+
206
+ def []=(k, v)
207
+ __send__("#{k}=", v)
208
+ end
209
+
210
+ def ==(o)
211
+ return false if o == nil
212
+ fields.values.all? {|fld| self[fld.name] == o[fld.name] }
213
+ end
214
+
215
+ def inspect
216
+ set = fields.values.select {|fld| self[fld.name] != nil }
217
+
218
+ flds = set.map do |fld|
219
+ val = self[fld.name]
220
+
221
+ case fld.type
222
+ when Class
223
+ "#{fld.name}: #{val.inspect}"
224
+ when Module
225
+ title = name_for(fld.type, val) || "-NA-"
226
+ "#{fld.name}: #{title}(#{val.inspect})"
227
+ else
228
+ "#{fld.name}: #{val.inspect}"
229
+ end
230
+ end
231
+
232
+ "<#{self.class.name} #{flds.join(", ")}>"
233
+ end
234
+
235
+ def to_hash
236
+ fields.values.inject({}) do |h, fld|
237
+ if v = self[fld.name]
238
+ h[fld.name] = v
239
+ end
240
+ h
241
+ end
242
+ end
243
+
244
+ end
245
+
246
+ end