beefcake-spanx 0.3.4.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.
@@ -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